From 9fe9f0b529e56a9e9eb194bc81c0126242e23d85 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Fri, 11 Apr 2025 00:36:21 +0700
Subject: [PATCH 01/15] Add search/filter text input to lists with items

---
 .../categories/WorkspaceCategoriesPage.tsx    | 41 ++++++++++++++++---
 1 file changed, 36 insertions(+), 5 deletions(-)

diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 3c807548c749..12e108e95ba9 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -52,6 +52,7 @@ import type SCREENS from '@src/SCREENS';
 import type {PolicyCategory} from '@src/types/onyx';
 import type DeepValueOf from '@src/types/utils/DeepValueOf';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import TextInput from '@components/TextInput';
 
 type PolicyOption = ListItem & {
     /** Category name is used as a key for the selectedCategories state */
@@ -84,6 +85,8 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
     const currentConnectionName = getCurrentConnectionName(policy);
     const isQuickSettingsFlow = !!backTo;
+    const [searchQuery, setSearchQuery] = useState('');
+    const [inputValue, setInputValue] = useState('');
 
     const canSelectMultiple = isSmallScreenWidth ? selectionMode?.isEnabled : true;
 
@@ -102,6 +105,23 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const cleanupSelectedOption = useCallback(() => setSelectedCategories({}), []);
     useCleanupSelectedOptions(cleanupSelectedOption);
 
+    const getSearchBar = () => {
+        return (
+            <View style={[{width: '300px', marginLeft: '20px'}]}>
+                <TextInput
+                    accessibilityLabel="Find category"
+                    placeholder='Find category'
+                    onChangeText={setInputValue}
+                    value={inputValue}
+                    icon={Expensicons.MagnifyingGlass}
+                    onSubmitEditing={() => setSearchQuery(inputValue)}
+                    shouldShowClearButton
+                />
+            </View>
+        );
+    };
+
+
     useEffect(() => {
         if (isEmptyObject(selectedCategories) || !canSelectMultiple) {
             return;
@@ -330,8 +350,18 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
         setSelectedCategories({});
     }, [setSelectedCategories, selectionMode?.isEnabled]);
 
-    const hasVisibleCategories = categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline);
+    const filteredCategoryList = useMemo(() => {
+        if (!searchQuery.trim()) {
+            return categoryList;
+        }
+        const lowerQuery = searchQuery.trim().toLowerCase();
+        return categoryList.filter(cat =>
+            cat.text?.toLowerCase().includes(lowerQuery)
+        );
+    }, [searchQuery, categoryList]);
+
 
+    const hasVisibleCategories = categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline);
     const getHeaderText = () => (
         <View style={[styles.ph5, styles.pb5, styles.pt3, shouldUseNarrowLayout ? styles.workspaceSectionMobile : styles.workspaceSection]}>
             {!hasSyncError && isConnectedToAccounting ? (
@@ -437,6 +467,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                     danger
                 />
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
+                {categoryList.length > 15 && getSearchBar()}
                 {(!shouldUseNarrowLayout || !hasVisibleCategories || isLoading) && getHeaderText()}
                 {isLoading && (
                     <ActivityIndicator
@@ -446,14 +477,14 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                     />
                 )}
 
-                {!hasVisibleCategories && !isLoading && (
+                {((!hasVisibleCategories && !isLoading) || filteredCategoryList.length === 0) && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
                             SkeletonComponent={TableListItemSkeleton}
                             headerMediaType={CONST.EMPTY_STATE_MEDIA.ANIMATION}
                             headerMedia={LottieAnimations.GenericEmptyState}
-                            title={translate('workspace.categories.emptyCategories.title')}
-                            subtitle={translate('workspace.categories.emptyCategories.subtitle')}
+                            title={filteredCategoryList.length === 0 ? translate('search.searchResults.emptyResults.title') : translate('workspace.categories.emptyCategories.title')}
+                            subtitle={filteredCategoryList.length === 0 ? 'Try adjusting your search criteria' : translate('workspace.categories.emptyCategories.subtitle')}
                             headerStyles={[styles.emptyStateCardIllustrationContainer, styles.emptyFolderBG]}
                             lottieWebViewStyles={styles.emptyStateFolderWebStyles}
                             headerContentStyles={styles.emptyStateFolderWebStyles}
@@ -465,7 +496,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={isSmallScreenWidth}
                         onTurnOnSelectionMode={(item) => item && toggleCategory(item)}
-                        sections={[{data: categoryList, isDisabled: false}]}
+                        sections={[{data: filteredCategoryList, isDisabled: false}]}
                         onCheckboxPress={toggleCategory}
                         onSelectRow={navigateToCategorySettings}
                         shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}

From 80ec5dadcefe8433572b8a8f6060a25b81d5f2f7 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Fri, 18 Apr 2025 17:40:50 +0700
Subject: [PATCH 02/15] fix: apply search bar to other pages

---
 src/components/SearchBar.tsx                  | 58 +++++++++++++++
 src/languages/en.ts                           |  5 ++
 src/languages/es.ts                           |  5 ++
 src/pages/workspace/WorkspaceMembersPage.tsx  | 73 ++++++++++++-------
 .../categories/WorkspaceCategoriesPage.tsx    | 44 ++++-------
 .../ReportFieldsListValuesPage.tsx            | 30 +++++++-
 .../WorkspaceReportFieldsPage.tsx             | 31 +++++++-
 .../workspace/tags/WorkspaceTagsPage.tsx      | 31 ++++++--
 .../workspace/tags/WorkspaceViewTagsPage.tsx  | 28 +++++--
 src/styles/index.ts                           |  6 ++
 10 files changed, 240 insertions(+), 71 deletions(-)
 create mode 100644 src/components/SearchBar.tsx

diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
new file mode 100644
index 000000000000..16ad089b37da
--- /dev/null
+++ b/src/components/SearchBar.tsx
@@ -0,0 +1,58 @@
+import React, {useState} from 'react';
+import type {StyleProp, ViewStyle} from 'react-native';
+import {View} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useThemeStyles from '@hooks/useThemeStyles';
+import CONST from '@src/CONST';
+import type IconAsset from '@src/types/utils/IconAsset';
+import {MagnifyingGlass} from './Icon/Expensicons';
+import Text from './Text';
+import TextInput from './TextInput';
+
+type SearchBarProps = {
+    label?: string;
+    icon?: IconAsset;
+    inputValue: string;
+    onChangeText?: (text: string) => void;
+    onSubmitEditing?: (text: string) => void;
+    style?: StyleProp<ViewStyle>;
+    shouldShowEmptyState?: boolean;
+};
+
+function SearchBar({label, style, icon = MagnifyingGlass, inputValue, onChangeText, onSubmitEditing, shouldShowEmptyState}: SearchBarProps) {
+    const styles = useThemeStyles();
+    const {shouldUseNarrowLayout} = useResponsiveLayout();
+    const {translate} = useLocalize();
+    const [isTextInputFocused, setIsTextInputFocused] = useState(false);
+
+    return (
+        <>
+            <View style={[styles.getSearchBarStyle(shouldUseNarrowLayout), style]}>
+                <TextInput
+                    label={label}
+                    accessibilityLabel={label}
+                    role={CONST.ROLE.PRESENTATION}
+                    value={inputValue}
+                    onChangeText={onChangeText}
+                    inputMode={CONST.INPUT_MODE.TEXT}
+                    selectTextOnFocus
+                    spellCheck={false}
+                    icon={inputValue?.length && isTextInputFocused ? undefined : icon}
+                    onSubmitEditing={() => onSubmitEditing?.(inputValue)}
+                    shouldShowClearButton
+                    onFocus={() => setIsTextInputFocused(true)}
+                    onBlur={() => setIsTextInputFocused(false)}
+                />
+            </View>
+            {!!shouldShowEmptyState && inputValue.length !== 0 && (
+                <View style={[styles.ph5, styles.pv3, styles.pb5]}>
+                    <Text style={[styles.textNormal, styles.colorMuted]}>{translate('common.noResultsFoundMatching', {searchString: inputValue})}</Text>
+                </View>
+            )}
+        </>
+    );
+}
+
+SearchBar.displayName = 'SearchBar';
+export default SearchBar;
diff --git a/src/languages/en.ts b/src/languages/en.ts
index ed17381a6378..f87046cdaced 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -365,6 +365,7 @@ const translations = {
         send: 'Send',
         na: 'N/A',
         noResultsFound: 'No results found',
+        noResultsFoundMatching: ({searchString}: {searchString: string}) => `No results found matching "${searchString}"`,
         recentDestinations: 'Recent destinations',
         timePrefix: "It's",
         conjunctionFor: 'for',
@@ -3902,6 +3903,7 @@ const translations = {
             addCategory: 'Add category',
             editCategory: 'Edit category',
             editCategories: 'Edit categories',
+            findCategory: 'Find category',
             categoryRequiredError: 'Category name is required',
             existingCategoryError: 'A category with this name already exists',
             invalidCategoryName: 'Invalid category name',
@@ -4076,6 +4078,7 @@ const translations = {
             addField: 'Add field',
             delete: 'Delete field',
             deleteFields: 'Delete fields',
+            findReportField: 'Find report field',
             deleteConfirmation: 'Are you sure you want to delete this report field?',
             deleteFieldsConfirmation: 'Are you sure you want to delete these report fields?',
             emptyReportFields: {
@@ -4132,6 +4135,7 @@ const translations = {
             addTag: 'Add tag',
             editTag: 'Edit tag',
             editTags: 'Edit tags',
+            findTag: 'Find tag',
             subtitle: 'Tags add more detailed ways to classify costs.',
             emptyTags: {
                 title: "You haven't created any tags",
@@ -4230,6 +4234,7 @@ const translations = {
                 one: 'Remove member',
                 other: 'Remove members',
             }),
+            findMember: 'Find member',
             removeWorkspaceMemberButtonTitle: 'Remove from workspace',
             removeGroupMemberButtonTitle: 'Remove from group',
             removeRoomMemberButtonTitle: 'Remove from chat',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index c08ebd3fdf06..3f14ead4136b 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -356,6 +356,7 @@ const translations = {
         send: 'Enviar',
         na: 'N/A',
         noResultsFound: 'No se han encontrado resultados',
+        noResultsFoundMatching: ({searchString}: {searchString: string}) => `No se encontraron resultados que coincidan con "${searchString}"`,
         recentDestinations: 'Destinos recientes',
         timePrefix: 'Son las',
         conjunctionFor: 'para',
@@ -3946,6 +3947,7 @@ const translations = {
             addCategory: 'Añadir categoría',
             editCategory: 'Editar categoría',
             editCategories: 'Editar categorías',
+            findCategory: 'Encontrar categoría',
             categoryRequiredError: 'Lo nombre de la categoría es obligatorio',
             existingCategoryError: 'Ya existe una categoría con este nombre',
             invalidCategoryName: 'Lo nombre de la categoría es invalido',
@@ -4123,6 +4125,7 @@ const translations = {
             addField: 'Añadir campo',
             delete: 'Eliminar campo',
             deleteFields: 'Eliminar campos',
+            findReportField: 'Encontrar campo del informe',
             deleteConfirmation: '¿Está seguro de que desea eliminar este campo del informe?',
             deleteFieldsConfirmation: '¿Está seguro de que desea eliminar estos campos del informe?',
             emptyReportFields: {
@@ -4179,6 +4182,7 @@ const translations = {
             addTag: 'Añadir etiqueta',
             editTag: 'Editar etiqueta',
             editTags: 'Editar etiquetas',
+            findTag: 'Encontrar etiquetas',
             subtitle: 'Las etiquetas añaden formas más detalladas de clasificar los costos.',
             emptyTags: {
                 title: 'No has creado ninguna etiqueta',
@@ -4277,6 +4281,7 @@ const translations = {
                 one: 'Eliminar miembro',
                 other: 'Eliminar miembros',
             }),
+            findMember: 'Encontrar miembro',
             removeWorkspaceMemberButtonTitle: 'Eliminar del espacio de trabajo',
             removeGroupMemberButtonTitle: 'Eliminar del grupo',
             removeRoomMemberButtonTitle: 'Eliminar del chat',
diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index af1d53fbdb15..d5f66f7f3718 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -15,6 +15,7 @@ import DecisionModal from '@components/DecisionModal';
 import {Download, FallbackAvatar, MakeAdmin, Plus, RemoveMembers, Table, User, UserEye} from '@components/Icon/Expensicons';
 import {ReceiptWrangler} from '@components/Icon/Illustrations';
 import MessagesRow from '@components/MessagesRow';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem, SelectionListHandle} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -99,6 +100,7 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
     const isOfflineAndNoMemberDataAvailable = isEmptyObject(policy?.employeeList) && isOffline;
     const prevPersonalDetails = usePrevious(personalDetails);
     const {translate, formatPhoneNumber, preferredLocale} = useLocalize();
+    const [inputValue, setInputValue] = useState('');
 
     // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type for the decision modal
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
@@ -464,6 +466,14 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
 
     const data = useMemo(() => getUsers(), [getUsers]);
 
+    const filteredData = useMemo(() => {
+        if (!inputValue.trim()) {
+            return data;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return data.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.alternateText?.toLowerCase().includes(lowerQuery));
+    }, [data, inputValue]);
+
     useEffect(() => {
         if (!isFocused) {
             return;
@@ -688,6 +698,15 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
             {() => (
                 <>
                     {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
+                    {shouldUseNarrowLayout ? <View style={[styles.pr5]}>{getHeaderContent()}</View> : getHeaderContent()}
+                    {data.length > 15 && (
+                        <SearchBar
+                            inputValue={inputValue}
+                            onChangeText={setInputValue}
+                            label={translate('workspace.people.findMember')}
+                            shouldShowEmptyState={!filteredData.length}
+                        />
+                    )}
                     <ConfirmModal
                         isVisible={isOfflineModalVisible}
                         onConfirm={() => setIsOfflineModalVisible(false)}
@@ -724,33 +743,33 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
                         isVisible={isDownloadFailureModalVisible}
                         onClose={() => setIsDownloadFailureModalVisible(false)}
                     />
-                    <View style={[styles.w100, styles.flex1]}>
-                        <SelectionListWithModal
-                            ref={selectionListRef}
-                            canSelectMultiple={canSelectMultiple}
-                            sections={[{data, isDisabled: false}]}
-                            ListItem={TableListItem}
-                            turnOnSelectionModeOnLongPress={isPolicyAdmin}
-                            onTurnOnSelectionMode={(item) => item && toggleUser(item?.accountID)}
-                            shouldUseUserSkeletonView
-                            disableKeyboardShortcuts={removeMembersConfirmModalVisible}
-                            headerMessage={getHeaderMessage()}
-                            headerContent={!shouldUseNarrowLayout && getHeaderContent()}
-                            onSelectRow={openMemberDetails}
-                            shouldSingleExecuteRowSelect={!isPolicyAdmin}
-                            onCheckboxPress={(item) => toggleUser(item.accountID)}
-                            onSelectAll={() => toggleAllUsers(data)}
-                            onDismissError={dismissError}
-                            showLoadingPlaceholder={isLoading}
-                            shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
-                            textInputRef={textInputRef}
-                            customListHeader={getCustomListHeader()}
-                            listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                            listHeaderContent={shouldUseNarrowLayout ? <View style={[styles.pr5]}>{getHeaderContent()}</View> : null}
-                            showScrollIndicator={false}
-                            addBottomSafeAreaPadding
-                        />
-                    </View>
+                    {!!filteredData.length && (
+                        <View style={[styles.w100, styles.flex1]}>
+                            <SelectionListWithModal
+                                ref={selectionListRef}
+                                canSelectMultiple={canSelectMultiple}
+                                sections={[{data: filteredData, isDisabled: false}]}
+                                ListItem={TableListItem}
+                                turnOnSelectionModeOnLongPress={isPolicyAdmin}
+                                onTurnOnSelectionMode={(item) => item && toggleUser(item?.accountID)}
+                                shouldUseUserSkeletonView
+                                disableKeyboardShortcuts={removeMembersConfirmModalVisible}
+                                headerMessage={getHeaderMessage()}
+                                onSelectRow={openMemberDetails}
+                                shouldSingleExecuteRowSelect={!isPolicyAdmin}
+                                onCheckboxPress={(item) => toggleUser(item.accountID)}
+                                onSelectAll={() => toggleAllUsers(filteredData)}
+                                onDismissError={dismissError}
+                                showLoadingPlaceholder={isLoading}
+                                shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
+                                textInputRef={textInputRef}
+                                customListHeader={getCustomListHeader()}
+                                listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
+                                showScrollIndicator={false}
+                                addBottomSafeAreaPadding
+                            />
+                        </View>
+                    )}
                 </>
             )}
         </WorkspacePageWithSections>
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index ff3f59b82ec5..228d977c6fc6 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -14,6 +14,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
 import LottieAnimations from '@components/LottieAnimations';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -21,7 +22,6 @@ import CustomListHeader from '@components/SelectionListWithModal/CustomListHeade
 import TableListItemSkeleton from '@components/Skeletons/TableRowSkeleton';
 import Switch from '@components/Switch';
 import Text from '@components/Text';
-import TextInput from '@components/TextInput';
 import TextLink from '@components/TextLink';
 import useAutoTurnSelectionModeOffWhenHasNoActiveOption from '@hooks/useAutoTurnSelectionModeOffWhenHasNoActiveOption';
 import useCleanupSelectedOptions from '@hooks/useCleanupSelectedOptions';
@@ -87,7 +87,6 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
     const currentConnectionName = getCurrentConnectionName(policy);
     const isQuickSettingsFlow = !!backTo;
-    const [searchQuery, setSearchQuery] = useState('');
     const [inputValue, setInputValue] = useState('');
     const {canUseLeftHandBar} = usePermissions();
 
@@ -108,22 +107,6 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const cleanupSelectedOption = useCallback(() => setSelectedCategories({}), []);
     useCleanupSelectedOptions(cleanupSelectedOption);
 
-    const getSearchBar = () => {
-        return (
-            <View style={[{width: '300px', marginLeft: '20px'}]}>
-                <TextInput
-                    accessibilityLabel="Find category"
-                    placeholder="Find category"
-                    onChangeText={setInputValue}
-                    value={inputValue}
-                    icon={Expensicons.MagnifyingGlass}
-                    onSubmitEditing={() => setSearchQuery(inputValue)}
-                    shouldShowClearButton
-                />
-            </View>
-        );
-    };
-
     useEffect(() => {
         if (isEmptyObject(selectedCategories) || !canSelectMultiple) {
             return;
@@ -353,12 +336,12 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     }, [setSelectedCategories, selectionMode?.isEnabled]);
 
     const filteredCategoryList = useMemo(() => {
-        if (!searchQuery.trim()) {
+        if (!inputValue.trim()) {
             return categoryList;
         }
-        const lowerQuery = searchQuery.trim().toLowerCase();
+        const lowerQuery = inputValue.trim().toLowerCase();
         return categoryList.filter((cat) => cat.text?.toLowerCase().includes(lowerQuery));
-    }, [searchQuery, categoryList]);
+    }, [inputValue, categoryList]);
 
     const hasVisibleCategories = categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline);
     const getHeaderText = () => (
@@ -472,8 +455,15 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                     danger
                 />
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
-                {categoryList.length > 15 && getSearchBar()}
-                {(!shouldUseNarrowLayout || !hasVisibleCategories || isLoading) && getHeaderText()}
+                {hasVisibleCategories && !isLoading && getHeaderText()}
+                {categoryList.length > 15 && (
+                    <SearchBar
+                        label={translate('workspace.categories.findCategory')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={hasVisibleCategories && !isLoading && filteredCategoryList.length === 0}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -481,15 +471,14 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                         color={theme.spinner}
                     />
                 )}
-
-                {((!hasVisibleCategories && !isLoading) || filteredCategoryList.length === 0) && (
+                {!hasVisibleCategories && !isLoading && inputValue.length === 0 && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
                             SkeletonComponent={TableListItemSkeleton}
                             headerMediaType={CONST.EMPTY_STATE_MEDIA.ANIMATION}
                             headerMedia={LottieAnimations.GenericEmptyState}
-                            title={filteredCategoryList.length === 0 ? translate('search.searchResults.emptyResults.title') : translate('workspace.categories.emptyCategories.title')}
-                            subtitle={filteredCategoryList.length === 0 ? 'Try adjusting your search criteria' : translate('workspace.categories.emptyCategories.subtitle')}
+                            title={translate('workspace.categories.emptyCategories.title')}
+                            subtitle={translate('workspace.categories.emptyCategories.subtitle')}
                             headerStyles={[styles.emptyStateCardIllustrationContainer, styles.emptyFolderBG]}
                             lottieWebViewStyles={styles.emptyStateFolderWebStyles}
                             headerContentStyles={styles.emptyStateFolderWebStyles}
@@ -510,7 +499,6 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                         onDismissError={dismissError}
                         customListHeader={getCustomListHeader()}
                         listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                        listHeaderContent={shouldUseNarrowLayout ? getHeaderText() : null}
                         showScrollIndicator={false}
                         addBottomSafeAreaPadding
                     />
diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
index 9c84adbafcc7..82498f4d6523 100644
--- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
+++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
@@ -11,6 +11,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
 import * as Illustrations from '@components/Icon/Illustrations';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -97,6 +98,8 @@ function ReportFieldsListValuesPage({
         return [reportFieldValues, reportFieldDisabledValues];
     }, [formDraft?.disabledListValues, formDraft?.listValues, policy?.fieldList, reportFieldID]);
 
+    const [inputValue, setInputValue] = useState('');
+
     const updateReportFieldListValueEnabled = useCallback(
         (value: boolean, valueIndex: number) => {
             if (reportFieldID) {
@@ -137,6 +140,21 @@ function ReportFieldsListValuesPage({
         return [{data, isDisabled: false}];
     }, [canSelectMultiple, disabledListValues, listValues, selectedValues, translate, updateReportFieldListValueEnabled]);
 
+    const filteredListValuesSections = useMemo(() => {
+        if (!inputValue.trim()) {
+            return listValuesSections;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return [
+            {
+                data: listValuesSections.at(0)?.data.filter((reportField) => reportField.text?.toLowerCase().includes(lowerQuery)) ?? [],
+                isDisabled: listValuesSections?.at(0)?.isDisabled,
+            },
+        ];
+    }, [listValuesSections, inputValue]);
+
+    const filteredListValuesArray = (filteredListValuesSections.at(0)?.data ?? []).map((item) => item.value);
+
     const shouldShowEmptyState = Object.values(listValues ?? {}).length <= 0;
     const selectedValuesArray = Object.keys(selectedValues).filter((key) => selectedValues[key]);
 
@@ -148,7 +166,7 @@ function ReportFieldsListValuesPage({
     };
 
     const toggleAllValues = () => {
-        setSelectedValues(selectedValuesArray.length > 0 ? {} : Object.fromEntries(listValues.map((value) => [value, true])));
+        setSelectedValues(selectedValuesArray.length > 0 ? {} : Object.fromEntries(filteredListValuesArray.map((value) => [value, true])));
     };
 
     const handleDeleteValues = () => {
@@ -325,6 +343,14 @@ function ReportFieldsListValuesPage({
                 <View style={[styles.ph5, styles.pv4]}>
                     <Text style={[styles.sidebarLinkText, styles.optionAlternateText]}>{translate('workspace.reportFields.listInputSubtitle')}</Text>
                 </View>
+                {(listValuesSections.at(0)?.data?.length ?? 0) > 1 && (
+                    <SearchBar
+                        label={translate('workspace.reportFields.findReportField')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredListValuesSections.at(0)?.data.length === 0}
+                    />
+                )}
                 {shouldShowEmptyState && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
@@ -344,7 +370,7 @@ function ReportFieldsListValuesPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!hasAccountingConnections}
                         onTurnOnSelectionMode={(item) => item && toggleValue(item)}
-                        sections={listValuesSections}
+                        sections={filteredListValuesSections}
                         onCheckboxPress={toggleValue}
                         onSelectRow={openListValuePage}
                         onSelectAll={toggleAllValues}
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index 4ae84c7b996b..9dcf97d1d3aa 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -14,6 +14,7 @@ import {Pencil} from '@components/Icon/Illustrations';
 import LottieAnimations from '@components/LottieAnimations';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import SearchBar from '@components/SearchBar';
 import ListItemRightCaretWithLabel from '@components/SelectionList/ListItemRightCaretWithLabel';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
@@ -90,6 +91,8 @@ function WorkspaceReportFieldsPage({
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
     const currentConnectionName = getCurrentConnectionName(policy);
 
+    const [inputValue, setInputValue] = useState('');
+
     const canSelectMultiple = !hasReportAccountingConnections && (isSmallScreenWidth ? selectionMode?.isEnabled : true);
 
     const fetchReportFields = useCallback(() => {
@@ -134,7 +137,20 @@ function WorkspaceReportFieldsPage({
         ];
     }, [filteredPolicyFieldList, policy, selectedReportFields, canSelectMultiple, translate]);
 
-    useAutoTurnSelectionModeOffWhenHasNoActiveOption(reportFieldsSections.at(0)?.data ?? ([] as ListItem[]));
+    const filteredReportFieldsSections = useMemo(() => {
+        if (!inputValue.trim()) {
+            return reportFieldsSections;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return [
+            {
+                data: reportFieldsSections.at(0)?.data.filter((reportField) => reportField.text.toLowerCase().includes(lowerQuery)) ?? [],
+                isDisabled: reportFieldsSections?.at(0)?.isDisabled,
+            },
+        ];
+    }, [reportFieldsSections, inputValue]);
+
+    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredReportFieldsSections.at(0)?.data ?? ([] as ListItem[]));
 
     const updateSelectedReportFields = (item: ReportFieldForList) => {
         const fieldKey = getReportFieldKey(item.fieldID);
@@ -273,6 +289,15 @@ function WorkspaceReportFieldsPage({
                     danger
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleReportField || isLoading) && getHeaderText()}
+                {!shouldShowEmptyState && !isLoading && shouldUseNarrowLayout && getHeaderText()}
+                {(reportFieldsSections.at(0)?.data?.length ?? 0) > 15 && (
+                    <SearchBar
+                        label={translate('workspace.reportFields.findReportField')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredReportFieldsSections.at(0)?.data.length === 0}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -280,7 +305,7 @@ function WorkspaceReportFieldsPage({
                         color={theme.spinner}
                     />
                 )}
-                {shouldShowEmptyState && (
+                {shouldShowEmptyState && filteredReportFieldsSections.at(0)?.data.length === 0 && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
                             title={translate('workspace.reportFields.emptyReportFields.title')}
@@ -299,7 +324,7 @@ function WorkspaceReportFieldsPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!hasReportAccountingConnections}
                         onTurnOnSelectionMode={(item) => item && updateSelectedReportFields(item)}
-                        sections={reportFieldsSections}
+                        sections={filteredReportFieldsSections}
                         onCheckboxPress={updateSelectedReportFields}
                         onSelectRow={navigateToReportFieldsSettings}
                         onSelectAll={toggleAllReportFields}
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index 6e3a2d258eb2..d3fcbaae8747 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -15,6 +15,7 @@ import LottieAnimations from '@components/LottieAnimations';
 import type {PopoverMenuItem} from '@components/PopoverMenu';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import SelectionListWithModal from '@components/SelectionListWithModal';
 import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
@@ -95,6 +96,8 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
 
     const {isOffline} = useNetwork({onReconnect: fetchTags});
 
+    const [inputValue, setInputValue] = useState('');
+
     useEffect(() => {
         fetchTags();
         // eslint-disable-next-line react-compiler/react-compiler
@@ -206,13 +209,21 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
         }));
     }, [isMultiLevelTags, policyTagLists, selectedTags, canSelectMultiple, translate, updateWorkspaceRequiresTag, updateWorkspaceTagEnabled]);
 
-    const tagListKeyedByName = useMemo(
+    const filteredTagList = useMemo<TagListItem[]>(() => {
+        if (!inputValue.trim()) {
+            return tagList;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return tagList.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.value?.toLowerCase().includes(lowerQuery));
+    }, [tagList, inputValue]);
+
+    const filteredTagListKeyedByName = useMemo(
         () =>
-            tagList.reduce<Record<string, TagListItem>>((acc, tag) => {
+            filteredTagList.reduce<Record<string, TagListItem>>((acc, tag) => {
                 acc[tag.value] = tag;
                 return acc;
             }, {}),
-        [tagList],
+        [filteredTagList],
     );
 
     const toggleTag = (tag: TagListItem) => {
@@ -223,7 +234,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
     };
 
     const toggleAllTags = () => {
-        const availableTags = tagList.filter((tag) => tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+        const availableTags = filteredTagList.filter((tag) => tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
         setSelectedTags(Object.keys(selectedTags).length > 0 ? {} : Object.fromEntries(availableTags.map((item) => [item.value, true])));
     };
 
@@ -310,7 +321,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
         let disabledTagCount = 0;
         const tagsToEnable: Record<string, {name: string; enabled: boolean}> = {};
         for (const tagName of selectedTagsArray) {
-            if (tagListKeyedByName[tagName]?.enabled) {
+            if (filteredTagListKeyedByName[tagName]?.enabled) {
                 enabledTagCount++;
                 tagsToDisable[tagName] = {
                     name: tagName,
@@ -478,6 +489,14 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
                     danger
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleTags || isLoading) && getHeaderText()}
+                {tagList.length > 15 && (
+                    <SearchBar
+                        label={translate('workspace.tags.findTag')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={hasVisibleTags && !isLoading && !filteredTagList.length}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -504,7 +523,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!isMultiLevelTags}
                         onTurnOnSelectionMode={(item) => item && toggleTag(item)}
-                        sections={[{data: tagList, isDisabled: false}]}
+                        sections={[{data: filteredTagList, isDisabled: false}]}
                         onCheckboxPress={toggleTag}
                         onSelectRow={navigateToTagSettings}
                         shouldSingleExecuteRowSelect={!canSelectMultiple}
diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
index 831260db3ace..0057d32bc402 100644
--- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
@@ -10,6 +10,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
 import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ScreenWrapper from '@components/ScreenWrapper';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import SelectionListWithModal from '@components/SelectionListWithModal';
 import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
@@ -69,6 +70,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
     const currentTagListName = useMemo(() => getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]);
     const currentPolicyTag = policyTags?.[currentTagListName];
     const isQuickSettingsFlow = !!backTo;
+    const [inputValue, setInputValue] = useState('');
 
     const fetchTags = useCallback(() => {
         openPolicyTagsPage(policyID);
@@ -128,15 +130,23 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
         [currentPolicyTag?.tags, selectedTags, canSelectMultiple, translate, updateWorkspaceTagEnabled],
     );
 
+    const filteredTagList = useMemo(() => {
+        if (!inputValue.trim()) {
+            return tagList;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return tagList.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.value?.toLowerCase().includes(lowerQuery));
+    }, [tagList, inputValue]);
+
     const hasDependentTags = useMemo(() => hasDependentTagsPolicyUtils(policy, policyTags), [policy, policyTags]);
 
     const tagListKeyedByName = useMemo(
         () =>
-            tagList.reduce<Record<string, TagListItem>>((acc, tag) => {
+            filteredTagList.reduce<Record<string, TagListItem>>((acc, tag) => {
                 acc[tag.value] = tag;
                 return acc;
             }, {}),
-        [tagList],
+        [filteredTagList],
     );
 
     if (!currentPolicyTag) {
@@ -151,7 +161,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
     };
 
     const toggleAllTags = () => {
-        const availableTags = tagList.filter((tag) => tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+        const availableTags = filteredTagList.filter((tag) => tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
         const anySelected = availableTags.some((tag) => !!selectedTags[tag.value]);
 
         setSelectedTags(anySelected ? {} : Object.fromEntries(availableTags.map((t) => [t.value, true])));
@@ -346,12 +356,20 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
                         color={theme.spinner}
                     />
                 )}
-                {tagList.length > 0 && !isLoading && (
+                {tagList.length > 15 && (
+                    <SearchBar
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        label={translate('workspace.tags.findTag')}
+                        shouldShowEmptyState={filteredTagList.length === 0 && !isLoading}
+                    />
+                )}
+                {filteredTagList.length > 0 && !isLoading && (
                     <SelectionListWithModal
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress
                         onTurnOnSelectionMode={(item) => item && toggleTag(item)}
-                        sections={[{data: tagList, isDisabled: false}]}
+                        sections={[{data: filteredTagList, isDisabled: false}]}
                         onCheckboxPress={toggleTag}
                         onSelectRow={navigateToTagSettings}
                         onSelectAll={toggleAllTags}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index a299a8965aa9..672b7c999420 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -5659,6 +5659,12 @@ const styles = (theme: ThemeColors) =>
             left: 0,
             right: 0,
         },
+
+        getSearchBarStyle: (shouldUseNarrowLayout: boolean) => ({
+            maxWidth: shouldUseNarrowLayout ? '100%' : 300,
+            marginHorizontal: 20,
+            marginBottom: 20,
+        }),
     } satisfies Styles);
 
 type ThemeStyles = ReturnType<typeof styles>;

From 96eea58ee27d2a5ac9d04936b8d3d0d680a1b649 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Fri, 18 Apr 2025 17:46:02 +0700
Subject: [PATCH 03/15] fix: lint

---
 src/pages/workspace/WorkspaceMembersPage.tsx                  | 4 ++--
 src/pages/workspace/categories/WorkspaceCategoriesPage.tsx    | 4 ++--
 .../workspace/reportFields/ReportFieldsListValuesPage.tsx     | 2 +-
 .../workspace/reportFields/WorkspaceReportFieldsPage.tsx      | 2 +-
 src/pages/workspace/tags/WorkspaceTagsPage.tsx                | 4 ++--
 src/pages/workspace/tags/WorkspaceViewTagsPage.tsx            | 2 +-
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index d5f66f7f3718..38428179dd33 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -111,9 +111,9 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
         [isOfflineAndNoMemberDataAvailable, personalDetails, policy?.employeeList],
     );
 
-    const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`);
+    const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`, {canBeMissing: true});
     const {selectionMode} = useMobileSelectionMode();
-    const [session] = useOnyx(ONYXKEYS.SESSION);
+    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
     const currentUserAccountID = Number(session?.accountID);
     const selectionListRef = useRef<SelectionListHandle>(null);
     const isFocused = useIsFocused();
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 228d977c6fc6..86d62f0797e2 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -80,8 +80,8 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const backTo = route.params?.backTo;
     const policy = usePolicy(policyId);
     const {selectionMode} = useMobileSelectionMode();
-    const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyId}`);
-    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`);
+    const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyId}`, {canBeMissing: false});
+    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`, {canBeMissing: true});
     const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy);
     const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
index 82498f4d6523..0ae0b3a80f25 100644
--- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
+++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
@@ -72,7 +72,7 @@ function ReportFieldsListValuesPage({
     // See https://github.com/Expensify/App/issues/48724 for more details
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
-    const [formDraft] = useOnyx(ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM_DRAFT);
+    const [formDraft] = useOnyx(ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM_DRAFT, {canBeMissing: true});
     const {selectionMode} = useMobileSelectionMode();
 
     const [selectedValues, setSelectedValues] = useState<Record<string, boolean>>({});
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index 9dcf97d1d3aa..5dad765c0a18 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -85,7 +85,7 @@ function WorkspaceReportFieldsPage({
     const [selectedReportFields, setSelectedReportFields] = useState<PolicyReportField[]>([]);
     const [deleteReportFieldsConfirmModalVisible, setDeleteReportFieldsConfirmModalVisible] = useState(false);
     const hasReportAccountingConnections = hasAccountingConnections(policy);
-    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`);
+    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`, {canBeMissing: true});
     const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy);
     const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index d3fcbaae8747..94a69278bf10 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -79,10 +79,10 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
     const policyID = route.params.policyID;
     const backTo = route.params.backTo;
     const policy = usePolicy(policyID);
-    const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`);
+    const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: false});
     const {selectionMode} = useMobileSelectionMode();
     const {environmentURL} = useEnvironment();
-    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`);
+    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`, {canBeMissing: true});
     const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy);
     const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
     const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0;
diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
index 0057d32bc402..7958d85454d0 100644
--- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
@@ -65,7 +65,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
     const policyID = route.params.policyID;
     const backTo = route.params.backTo;
     const policy = usePolicy(policyID);
-    const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`);
+    const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: false});
     const {selectionMode} = useMobileSelectionMode();
     const currentTagListName = useMemo(() => getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]);
     const currentPolicyTag = policyTags?.[currentTagListName];

From 9d9a33ab877b0cb466ac9fc11d1c05da8b0606c6 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Mon, 21 Apr 2025 10:22:31 +0700
Subject: [PATCH 04/15] fix: handle selection for search results

---
 .../categories/WorkspaceCategoriesPage.tsx    | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 86d62f0797e2..85befe0342b1 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -169,7 +169,15 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
         }, []);
     }, [policyCategories, isOffline, selectedCategories, canSelectMultiple, translate, updateWorkspaceRequiresCategory]);
 
-    useAutoTurnSelectionModeOffWhenHasNoActiveOption(categoryList);
+    const filteredCategoryList = useMemo(() => {
+        if (!inputValue.trim()) {
+            return categoryList;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return categoryList.filter((cat) => cat.text?.toLowerCase().includes(lowerQuery));
+    }, [inputValue, categoryList]);
+
+    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredCategoryList);
 
     const toggleCategory = useCallback((category: PolicyOption) => {
         setSelectedCategories((prev) => {
@@ -182,7 +190,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     }, []);
 
     const toggleAllCategories = () => {
-        const availableCategories = categoryList.filter((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+        const availableCategories = filteredCategoryList.filter((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
         const someSelected = availableCategories.some((category) => selectedCategories[category.keyForList]);
         setSelectedCategories(someSelected ? {} : Object.fromEntries(availableCategories.map((item) => [item.keyForList, true])));
     };
@@ -335,14 +343,6 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
         setSelectedCategories({});
     }, [setSelectedCategories, selectionMode?.isEnabled]);
 
-    const filteredCategoryList = useMemo(() => {
-        if (!inputValue.trim()) {
-            return categoryList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return categoryList.filter((cat) => cat.text?.toLowerCase().includes(lowerQuery));
-    }, [inputValue, categoryList]);
-
     const hasVisibleCategories = categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline);
     const getHeaderText = () => (
         <View style={[styles.ph5, styles.pb5, styles.pt3, shouldUseNarrowLayout ? styles.workspaceSectionMobile : styles.workspaceSection]}>

From 3e097a1e8126916163304be3e86e11bd557c5d10 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Tue, 22 Apr 2025 22:51:53 +0700
Subject: [PATCH 05/15] fix: change button icon behavior

---
 src/components/SearchBar.tsx | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 16ad089b37da..62a80e137278 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -24,7 +24,6 @@ function SearchBar({label, style, icon = MagnifyingGlass, inputValue, onChangeTe
     const styles = useThemeStyles();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
     const {translate} = useLocalize();
-    const [isTextInputFocused, setIsTextInputFocused] = useState(false);
 
     return (
         <>
@@ -38,11 +37,11 @@ function SearchBar({label, style, icon = MagnifyingGlass, inputValue, onChangeTe
                     inputMode={CONST.INPUT_MODE.TEXT}
                     selectTextOnFocus
                     spellCheck={false}
-                    icon={inputValue?.length && isTextInputFocused ? undefined : icon}
+                    icon={inputValue?.length ? undefined : icon}
+                    iconContainerStyle={styles.p0}
                     onSubmitEditing={() => onSubmitEditing?.(inputValue)}
                     shouldShowClearButton
-                    onFocus={() => setIsTextInputFocused(true)}
-                    onBlur={() => setIsTextInputFocused(false)}
+                    shouldHideClearButton={!inputValue?.length}
                 />
             </View>
             {!!shouldShowEmptyState && inputValue.length !== 0 && (

From 0999cf88bcd29a3baf427130ebdf81ceedf3fe2c Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Tue, 22 Apr 2025 22:53:33 +0700
Subject: [PATCH 06/15] fix: lint

---
 src/components/SearchBar.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 62a80e137278..0f9a289e0f0a 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -1,4 +1,4 @@
-import React, {useState} from 'react';
+import React from 'react';
 import type {StyleProp, ViewStyle} from 'react-native';
 import {View} from 'react-native';
 import useLocalize from '@hooks/useLocalize';

From 2788e70de66fe8c2478e2f589c9493eb7a16bc62 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Wed, 23 Apr 2025 15:43:25 +0700
Subject: [PATCH 07/15] feat: add search bar to distance rate and tax page

---
 src/languages/en.ts                           |  4 ++
 src/languages/es.ts                           |  4 ++
 .../WorkspaceCompanyCardsList.tsx             | 48 ++++++++++++++----
 .../distanceRates/PolicyDistanceRatesPage.tsx | 25 ++++++++--
 .../WorkspaceExpensifyCardListPage.tsx        | 49 +++++++++++++++----
 .../workspace/taxes/WorkspaceTaxesPage.tsx    | 31 ++++++++++--
 6 files changed, 133 insertions(+), 28 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 4726aa4a4a21..cba956c2499b 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -3790,6 +3790,7 @@ const translations = {
                 },
             },
             assignCard: 'Assign card',
+            findCard: 'Find card',
             cardNumber: 'Card number',
             commercialFeed: 'Commercial feed',
             feedName: ({feedName}: CompanyCardFeedNameParams) => `${feedName} cards`,
@@ -3824,6 +3825,7 @@ const translations = {
             disclaimer:
                 'The Expensify Visa® Commercial Card is issued by The Bancorp Bank, N.A., Member FDIC, pursuant to a license from Visa U.S.A. Inc. and may not be used at all merchants that accept Visa cards. Apple® and the Apple logo® are trademarks of Apple Inc., registered in the U.S. and other countries. App Store is a service mark of Apple Inc. Google Play and the Google Play logo are trademarks of Google LLC.',
             issueCard: 'Issue card',
+            findCard: 'Find Expensify card',
             newCard: 'New card',
             name: 'Name',
             lastFour: 'Last 4',
@@ -4179,6 +4181,7 @@ const translations = {
             value: 'Value',
             taxReclaimableOn: 'Tax reclaimable on',
             taxRate: 'Tax rate',
+            findTaxRate: 'Find tax rate',
             error: {
                 taxRateAlreadyExists: 'This tax name is already in use',
                 taxCodeAlreadyExists: 'This tax code is already in use',
@@ -4599,6 +4602,7 @@ const translations = {
             centrallyManage: 'Centrally manage rates, track in miles or kilometers, and set a default category.',
             rate: 'Rate',
             addRate: 'Add rate',
+            findRate: 'Find rate',
             trackTax: 'Track tax',
             deleteRates: () => ({
                 one: 'Delete rate',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 5fb1aab958d1..6820017ca5b1 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -3831,6 +3831,7 @@ const translations = {
                 },
             },
             assignCard: 'Asignar tarjeta',
+            findCard: 'Encontrar tarjeta',
             cardNumber: 'Número de la tarjeta',
             commercialFeed: 'Fuente comercial',
             feedName: ({feedName}: CompanyCardFeedNameParams) => `Tarjetas ${feedName}`,
@@ -3865,6 +3866,7 @@ const translations = {
             disclaimer:
                 'La tarjeta comercial Expensify Visa® es emitida por The Bancorp Bank, N.A., miembro de la FDIC, en virtud de una licencia de Visa U.S.A. Inc. y no puede utilizarse en todos los comercios que aceptan tarjetas Visa. Apple® y el logotipo de Apple® son marcas comerciales de Apple Inc. registradas en EE.UU. y otros países. App Store es una marca de servicio de Apple Inc. Google Play y el logotipo de Google Play son marcas comerciales de Google LLC.',
             issueCard: 'Emitir tarjeta',
+            findCard: 'Encontrar Tarjetas Expensify',
             newCard: 'Nueva tarjeta',
             name: 'Nombre',
             lastFour: '4 últimos',
@@ -4225,6 +4227,7 @@ const translations = {
             customTaxName: 'Nombre del impuesto',
             value: 'Valor',
             taxRate: 'Tasa de impuesto',
+            findTaxRate: 'Encontrar tasa de impuesto',
             taxReclaimableOn: 'Impuesto recuperable en',
             error: {
                 taxRateAlreadyExists: 'Ya existe un impuesto con este nombre',
@@ -4647,6 +4650,7 @@ const translations = {
             centrallyManage: 'Gestiona centralizadamente las tasas, elige si contabilizar en millas o kilómetros, y define una categoría por defecto',
             rate: 'Tasa',
             addRate: 'Agregar tasa',
+            findRate: 'Encontrar tasa',
             trackTax: 'Impuesto de seguimiento',
             deleteRates: () => ({
                 one: 'Eliminar tasa',
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
index 66239d55ddb2..27bebcb93d7a 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
@@ -1,10 +1,11 @@
-import React, {useCallback, useMemo} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
 import type {ListRenderItemInfo} from 'react-native';
 import {FlatList, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import type {OnyxEntry} from 'react-native-onyx';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import {PressableWithFeedback} from '@components/Pressable';
+import SearchBar from '@components/SearchBar';
 import Text from '@components/Text';
 import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
@@ -34,11 +35,26 @@ type WorkspaceCompanyCardsListProps = {
 function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDisabledAssignCardButton}: WorkspaceCompanyCardsListProps) {
     const styles = useThemeStyles();
     const {translate} = useLocalize();
-    const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
-    const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES);
+    const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
+    const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true});
 
     const sortedCards = useMemo(() => sortCardsByCardholderName(cardsList, personalDetails), [cardsList, personalDetails]);
 
+    const [inputValue, setInputValue] = useState('');
+    const filteredSortedCards = useMemo(() => {
+        if (!inputValue) {
+            return sortedCards;
+        }
+        const lowerValue = inputValue.toLowerCase();
+        return sortedCards.filter((card) => {
+            const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
+            const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
+            const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
+            const accountName = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.displayName?.toLowerCase() ?? '';
+            return cardTitle.includes(lowerValue) || lastFourPAN.includes(lowerValue) || accountLogin.includes(lowerValue) || accountName.includes(lowerValue);
+        });
+    }, [inputValue, personalDetails, sortedCards]);
+
     const renderItem = useCallback(
         ({item, index}: ListRenderItemInfo<Card>) => {
             const cardID = Object.keys(cardsList ?? {}).find((id) => cardsList?.[id].cardID === item.cardID);
@@ -104,14 +120,26 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
         );
     }
 
+    const isSearchEmpty = filteredSortedCards.length === 0 && inputValue.length > 0;
+
     return (
-        <FlatList
-            contentContainerStyle={styles.flexGrow1}
-            data={sortedCards}
-            renderItem={renderItem}
-            ListHeaderComponent={renderListHeader}
-            stickyHeaderIndices={[0]}
-        />
+        <>
+            {sortedCards.length > 0 && (
+                <SearchBar
+                    label={translate('workspace.companyCards.findCard')}
+                    inputValue={inputValue}
+                    onChangeText={setInputValue}
+                    shouldShowEmptyState={isSearchEmpty}
+                />
+            )}
+            <FlatList
+                contentContainerStyle={styles.flexGrow1}
+                data={filteredSortedCards}
+                renderItem={renderItem}
+                ListHeaderComponent={!isSearchEmpty ? renderListHeader : null}
+                stickyHeaderIndices={[0]}
+            />
+        </>
     );
 }
 
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index 5adbb3a092ea..3acf5aa77405 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -8,6 +8,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
 import * as Expensicons from '@components/Icon/Expensicons';
 import * as Illustrations from '@components/Icon/Illustrations';
 import ScreenWrapper from '@components/ScreenWrapper';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -63,6 +64,7 @@ function PolicyDistanceRatesPage({
     const isFocused = useIsFocused();
     const policy = usePolicy(policyID);
     const {selectionMode} = useMobileSelectionMode();
+    const [inputValue, setInputValue] = useState('');
 
     const canSelectMultiple = shouldUseNarrowLayout ? selectionMode?.isEnabled : true;
 
@@ -177,6 +179,16 @@ function PolicyDistanceRatesPage({
         [customUnitRates, translate, customUnit, selectedDistanceRates, canSelectMultiple, policy?.pendingAction, updateDistanceRateEnabled],
     );
 
+    const filteredDistanceRatesList = useMemo<RateForList[]>(() => {
+        if (!inputValue.trim()) {
+            return distanceRatesList;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return distanceRatesList.filter((rate) => rate.text?.toLowerCase().includes(lowerQuery));
+    }, [distanceRatesList, inputValue]);
+
+    const hasVisibleRates = useMemo(() => Object.values(customUnitRates).some((rate) => rate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [customUnitRates]);
+
     const addRate = () => {
         Navigation.navigate(ROUTES.WORKSPACE_CREATE_DISTANCE_RATE.getRoute(policyID));
     };
@@ -363,7 +375,15 @@ function PolicyDistanceRatesPage({
                     {!shouldUseNarrowLayout && headerButtons}
                 </HeaderWithBackButton>
                 {shouldUseNarrowLayout && <View style={[styles.ph5]}>{headerButtons}</View>}
-                {!shouldUseNarrowLayout && getHeaderText()}
+                {Object.values(customUnitRates).length > 0 && getHeaderText()}
+                {Object.values(customUnitRates).length > 15 && (
+                    <SearchBar
+                        label={translate('workspace.distanceRates.findRate')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={hasVisibleRates && filteredDistanceRatesList.length === 0}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -377,7 +397,7 @@ function PolicyDistanceRatesPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress
                         onTurnOnSelectionMode={(item) => item && toggleRate(item)}
-                        sections={[{data: distanceRatesList, isDisabled: false}]}
+                        sections={[{data: filteredDistanceRatesList, isDisabled: false}]}
                         onCheckboxPress={toggleRate}
                         onSelectRow={openRateDetails}
                         onSelectAll={toggleAllRates}
@@ -386,7 +406,6 @@ function PolicyDistanceRatesPage({
                         shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
                         customListHeader={getCustomListHeader()}
                         listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                        listHeaderContent={shouldUseNarrowLayout ? getHeaderText() : null}
                         showScrollIndicator={false}
                     />
                 )}
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index 17f3ad65ae60..21b8c6f0b75f 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -13,6 +13,7 @@ import {HandCard} from '@components/Icon/Illustrations';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import {PressableWithFeedback} from '@components/Pressable';
 import ScreenWrapper from '@components/ScreenWrapper';
+import SearchBar from '@components/SearchBar';
 import useBottomSafeSafeAreaPaddingStyle from '@hooks/useBottomSafeSafeAreaPaddingStyle';
 import useExpensifyCardFeeds from '@hooks/useExpensifyCardFeeds';
 import useLocalize from '@hooks/useLocalize';
@@ -54,14 +55,14 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
     const policyID = route.params.policyID;
     const policy = usePolicy(policyID);
     const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
-    const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
-    const [cardOnWaitlist] = useOnyx(`${ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST}${policyID}`);
-    const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${fundID}`);
+    const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
+    const [cardOnWaitlist] = useOnyx(`${ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST}${policyID}`, {canBeMissing: true});
+    const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${fundID}`, {canBeMissing: true});
     const allExpensifyCardFeeds = useExpensifyCardFeeds(policyID);
 
     const shouldShowSelector = Object.keys(allExpensifyCardFeeds ?? {}).length > 1;
 
-    const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate});
+    const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate, canBeMissing: false});
     const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);
 
     const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout;
@@ -72,6 +73,22 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
 
     const sortedCards = useMemo(() => sortCardsByCardholderName(cardsList, personalDetails), [cardsList, personalDetails]);
 
+    const [inputValue, setInputValue] = useState('');
+
+    const filteredSortedCards = useMemo(() => {
+        if (!inputValue) {
+            return sortedCards;
+        }
+        const lowerValue = inputValue.toLowerCase();
+        return sortedCards.filter((card) => {
+            const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
+            const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
+            const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
+            const accountName = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.displayName?.toLowerCase() ?? '';
+            return cardTitle.includes(lowerValue) || lastFourPAN.includes(lowerValue) || accountLogin.includes(lowerValue) || accountName.includes(lowerValue);
+        });
+    }, [inputValue, sortedCards, personalDetails]);
+
     const handleIssueCardPress = () => {
         if (isActingAsDelegate) {
             setIsNoDelegateAccessMenuVisible(true);
@@ -141,6 +158,8 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
 
     const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle();
 
+    const isSearchEmpty = filteredSortedCards.length === 0 && inputValue.length > 0;
+
     return (
         <ScreenWrapper
             enableEdgeToEdgeBottomSafeAreaPadding
@@ -173,12 +192,22 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
             {isEmptyObject(cardsList) ? (
                 <EmptyCardView isBankAccountVerified={isBankAccountVerified} />
             ) : (
-                <FlatList
-                    data={sortedCards}
-                    renderItem={renderItem}
-                    ListHeaderComponent={renderListHeader}
-                    contentContainerStyle={bottomSafeAreaPaddingStyle}
-                />
+                <>
+                    {sortedCards.length > 15 && (
+                        <SearchBar
+                            inputValue={inputValue}
+                            onChangeText={setInputValue}
+                            shouldShowEmptyState={isSearchEmpty}
+                            label={translate('workspace.expensifyCard.findCard')}
+                        />
+                    )}
+                    <FlatList
+                        data={filteredSortedCards}
+                        renderItem={renderItem}
+                        ListHeaderComponent={!isSearchEmpty ? renderListHeader : null}
+                        contentContainerStyle={bottomSafeAreaPaddingStyle}
+                    />
+                </>
             )}
             <DelegateNoAccessModal
                 isNoDelegateAccessMenuVisible={isNoDelegateAccessMenuVisible}
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index 66112d0baa9e..0f9750ea36c3 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -9,6 +9,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
 import * as Expensicons from '@components/Icon/Expensicons';
 import * as Illustrations from '@components/Icon/Illustrations';
 import ScreenWrapper from '@components/ScreenWrapper';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -65,7 +66,7 @@ function WorkspaceTaxesPage({
     const defaultExternalID = policy?.taxRates?.defaultExternalID;
     const foreignTaxDefault = policy?.taxRates?.foreignTaxDefault;
     const hasAccountingConnections = hasAccountingConnectionsPolicyUtils(policy);
-    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`);
+    const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`, {canBeMissing: true});
     const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy);
     const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
 
@@ -75,6 +76,7 @@ function WorkspaceTaxesPage({
 
     const enabledRatesCount = selectedTaxesIDs.filter((taxID) => !policy?.taxRates?.taxes[taxID]?.isDisabled).length;
     const disabledRatesCount = selectedTaxesIDs.length - enabledRatesCount;
+    const [inputValue, setInputValue] = useState('');
 
     const fetchTaxes = useCallback(() => {
         openPolicyTaxesPage(policyID);
@@ -176,6 +178,18 @@ function WorkspaceTaxesPage({
             .sort((a, b) => (a.text ?? a.keyForList ?? '').localeCompare(b.text ?? b.keyForList ?? ''));
     }, [policy, textForDefault, selectedTaxesIDs, canSelectMultiple, translate, updateWorkspaceTaxEnabled]);
 
+    const filteredTaxesList = useMemo(() => {
+        if (!inputValue) {
+            return taxesList;
+        }
+        const lowerQuery = inputValue.toLowerCase();
+        return taxesList.filter((tax) => {
+            const taxName = tax.text?.toLowerCase() ?? '';
+            const taxAlternateText = tax.alternateText?.toLowerCase() ?? '';
+            return taxName.includes(lowerQuery) || taxAlternateText.includes(lowerQuery);
+        });
+    }, [inputValue, taxesList]);
+
     const isLoading = !isOffline && taxesList === undefined;
 
     const toggleTax = (tax: ListItem) => {
@@ -193,7 +207,7 @@ function WorkspaceTaxesPage({
     };
 
     const toggleAllTaxes = () => {
-        const taxesToSelect = taxesList.filter(
+        const taxesToSelect = filteredTaxesList.filter(
             (tax) => tax.keyForList !== defaultExternalID && tax.keyForList !== foreignTaxDefault && tax.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
         );
         setSelectedTaxesIDs((prev) => {
@@ -363,7 +377,15 @@ function WorkspaceTaxesPage({
                 </HeaderWithBackButton>
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{headerButtons}</View>}
 
-                {!shouldUseNarrowLayout && getHeaderText()}
+                {getHeaderText()}
+                {taxesList.length > 15 && (
+                    <SearchBar
+                        label={translate('workspace.taxes.findTaxRate')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={filteredTaxesList.length === 0}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -375,13 +397,12 @@ function WorkspaceTaxesPage({
                     canSelectMultiple={canSelectMultiple}
                     turnOnSelectionModeOnLongPress
                     onTurnOnSelectionMode={(item) => item && toggleTax(item)}
-                    sections={[{data: taxesList, isDisabled: false}]}
+                    sections={[{data: filteredTaxesList, isDisabled: false}]}
                     onCheckboxPress={toggleTax}
                     onSelectRow={navigateToEditTaxRate}
                     onSelectAll={toggleAllTaxes}
                     ListItem={TableListItem}
                     customListHeader={getCustomListHeader()}
-                    listHeaderContent={shouldUseNarrowLayout ? getHeaderText() : null}
                     shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
                     listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
                     onDismissError={(item) => (item.keyForList ? clearTaxRateError(policyID, item.keyForList, item.pendingAction) : undefined)}

From f0db5122e479139d1a8291bd8861df531e8007af Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Wed, 23 Apr 2025 16:30:36 +0700
Subject: [PATCH 08/15] fix: correct search condition in company card page

---
 src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
index 27bebcb93d7a..32fe2d3e43dd 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
@@ -124,7 +124,7 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
 
     return (
         <>
-            {sortedCards.length > 0 && (
+            {sortedCards.length > 15 && (
                 <SearchBar
                     label={translate('workspace.companyCards.findCard')}
                     inputValue={inputValue}

From f8c7941b8895823e00191274ad31a7af1bc1d545 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Fri, 25 Apr 2025 14:10:18 +0700
Subject: [PATCH 09/15] fix: change component styles and placement

---
 src/languages/en.ts                           |  2 +-
 src/languages/es.ts                           |  2 +-
 .../WorkspaceCompanyCardsList.tsx             |  3 +-
 .../expensifyCard/WorkspaceCardListHeader.tsx | 74 +---------------
 .../expensifyCard/WorkspaceCardListLabels.tsx | 87 +++++++++++++++++++
 .../WorkspaceExpensifyCardListPage.tsx        | 36 ++++----
 6 files changed, 111 insertions(+), 93 deletions(-)
 create mode 100644 src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx

diff --git a/src/languages/en.ts b/src/languages/en.ts
index f27cc7fe339e..1e6d5cab8c29 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -3833,7 +3833,7 @@ const translations = {
             disclaimer:
                 'The Expensify Visa® Commercial Card is issued by The Bancorp Bank, N.A., Member FDIC, pursuant to a license from Visa U.S.A. Inc. and may not be used at all merchants that accept Visa cards. Apple® and the Apple logo® are trademarks of Apple Inc., registered in the U.S. and other countries. App Store is a service mark of Apple Inc. Google Play and the Google Play logo are trademarks of Google LLC.',
             issueCard: 'Issue card',
-            findCard: 'Find Expensify card',
+            findCard: 'Find card',
             newCard: 'New card',
             name: 'Name',
             lastFour: 'Last 4',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 948ca8acfe96..73db00f555f3 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -3876,7 +3876,7 @@ const translations = {
             disclaimer:
                 'La tarjeta comercial Expensify Visa® es emitida por The Bancorp Bank, N.A., miembro de la FDIC, en virtud de una licencia de Visa U.S.A. Inc. y no puede utilizarse en todos los comercios que aceptan tarjetas Visa. Apple® y el logotipo de Apple® son marcas comerciales de Apple Inc. registradas en EE.UU. y otros países. App Store es una marca de servicio de Apple Inc. Google Play y el logotipo de Google Play son marcas comerciales de Google LLC.',
             issueCard: 'Emitir tarjeta',
-            findCard: 'Encontrar Tarjetas Expensify',
+            findCard: 'Encontrar tarjeta',
             newCard: 'Nueva tarjeta',
             name: 'Nombre',
             lastFour: '4 últimos',
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
index 32fe2d3e43dd..7f5af7c969cf 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
@@ -124,12 +124,13 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
 
     return (
         <>
-            {sortedCards.length > 15 && (
+            {sortedCards.length > 0 && (
                 <SearchBar
                     label={translate('workspace.companyCards.findCard')}
                     inputValue={inputValue}
                     onChangeText={setInputValue}
                     shouldShowEmptyState={isSearchEmpty}
+                    style={[styles.mt5]}
                 />
             )}
             <FlatList
diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx
index 9bab132b174f..efc7a8f99674 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx
@@ -1,102 +1,32 @@
 import React from 'react';
 import {View} from 'react-native';
-import {useOnyx} from 'react-native-onyx';
 import FormHelpMessage from '@components/FormHelpMessage';
 import Text from '@components/Text';
 import useLocalize from '@hooks/useLocalize';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
 import {getLatestErrorMessage} from '@libs/ErrorUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
 import type {ExpensifyCardSettings} from '@src/types/onyx';
-import WorkspaceCardsListLabel from './WorkspaceCardsListLabel';
 
 type WorkspaceCardListHeaderProps = {
-    /** ID of the current policy */
-    policyID: string;
-
     /** Card settings */
     cardSettings: ExpensifyCardSettings | undefined;
 };
 
-function WorkspaceCardListHeader({policyID, cardSettings}: WorkspaceCardListHeaderProps) {
+function WorkspaceCardListHeader({cardSettings}: WorkspaceCardListHeaderProps) {
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {shouldUseNarrowLayout, isMediumScreenWidth, isSmallScreenWidth} = useResponsiveLayout();
     const styles = useThemeStyles();
     const {translate} = useLocalize();
 
-    const workspaceAccountID = useWorkspaceAccountID(policyID);
     const isLessThanMediumScreen = isMediumScreenWidth || isSmallScreenWidth;
 
-    const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`);
-
     const errorMessage = getLatestErrorMessage(cardSettings) ?? '';
 
-    const shouldShowSettlementButtonOrDate = !!cardSettings?.isMonthlySettlementAllowed || cardManualBilling;
-
-    const getLabelsLayout = () => {
-        if (!isLessThanMediumScreen) {
-            return (
-                <>
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
-                    />
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
-                    />
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
-                    />
-                </>
-            );
-        }
-        return shouldShowSettlementButtonOrDate ? (
-            <>
-                <WorkspaceCardsListLabel
-                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
-                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
-                />
-                <View style={[styles.flexRow, !isLessThanMediumScreen && styles.flex2, isLessThanMediumScreen && styles.mt5]}>
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
-                    />
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
-                    />
-                </View>
-            </>
-        ) : (
-            <>
-                <View style={[styles.flexRow, isLessThanMediumScreen && styles.mb5]}>
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
-                    />
-                    <WorkspaceCardsListLabel
-                        type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
-                        value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
-                    />
-                </View>
-                <WorkspaceCardsListLabel
-                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
-                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
-                />
-            </>
-        );
-    };
-
     return (
         <View style={styles.appBG}>
-            <View style={[isLessThanMediumScreen ? styles.flexColumn : styles.flexRow, styles.mt5, styles.mh5, styles.ph4]}>{getLabelsLayout()}</View>
             {!!errorMessage && (
-                <View style={[styles.mh5, styles.ph4, styles.mt2]}>
+                <View style={[styles.mh5, styles.pr4, styles.mt2]}>
                     <FormHelpMessage
                         isError
                         message={errorMessage}
diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
new file mode 100644
index 000000000000..092266a559c6
--- /dev/null
+++ b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import {View} from 'react-native';
+import {useOnyx} from 'react-native-onyx';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useThemeStyles from '@hooks/useThemeStyles';
+import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {ExpensifyCardSettings} from '@src/types/onyx';
+import WorkspaceCardsListLabel from './WorkspaceCardsListLabel';
+
+type WorkspaceCardListLabelsProps = {
+    /** ID of the current policy */
+    policyID: string;
+
+    /** Card settings */
+    cardSettings: ExpensifyCardSettings | undefined;
+};
+
+function WorkspaceCardListLabels({policyID, cardSettings}: WorkspaceCardListLabelsProps) {
+    // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
+    const {isMediumScreenWidth, isSmallScreenWidth} = useResponsiveLayout();
+    const styles = useThemeStyles();
+
+    const workspaceAccountID = useWorkspaceAccountID(policyID);
+
+    const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`);
+    const shouldShowSettlementButtonOrDate = !!cardSettings?.isMonthlySettlementAllowed || cardManualBilling;
+
+    const isLessThanMediumScreen = isMediumScreenWidth || isSmallScreenWidth;
+    if (!isLessThanMediumScreen) {
+        return (
+            <View style={[styles.flexRow, styles.mt5, styles.mh5, styles.pr4]}>
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
+                />
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
+                />
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
+                />
+            </View>
+        );
+    }
+    return shouldShowSettlementButtonOrDate ? (
+        <View style={[styles.flexColumn, styles.mt5, styles.mh5, styles.pr4]}>
+            <WorkspaceCardsListLabel
+                type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
+                value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
+            />
+            <View style={[styles.flexRow, !isLessThanMediumScreen && styles.flex2, isLessThanMediumScreen && styles.mt5]}>
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
+                />
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
+                />
+            </View>
+        </View>
+    ) : (
+        <View style={[styles.flexColumn, styles.mt5, styles.mh5, styles.pr4]}>
+            <View style={[styles.flexRow, isLessThanMediumScreen && styles.mb5]}>
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
+                />
+                <WorkspaceCardsListLabel
+                    type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
+                    value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
+                />
+            </View>
+            <WorkspaceCardsListLabel
+                type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK}
+                value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CASH_BACK] ?? 0}
+            />
+        </View>
+    );
+}
+
+WorkspaceCardListLabels.displayName = 'WorkspaceCardListLabels';
+export default WorkspaceCardListLabels;
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index 87fe958971cf..b6617892b4d2 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -35,6 +35,7 @@ import type {Card, WorkspaceCardsList} from '@src/types/onyx';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import EmptyCardView from './EmptyCardView';
 import WorkspaceCardListHeader from './WorkspaceCardListHeader';
+import WorkspaceCardListLabels from './WorkspaceCardListLabels';
 import WorkspaceCardListRow from './WorkspaceCardListRow';
 
 type WorkspaceExpensifyCardListPageProps = {
@@ -147,19 +148,11 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
         [personalDetails, policyCurrency, policyID, workspaceAccountID, styles],
     );
 
-    const renderListHeader = useCallback(
-        () => (
-            <WorkspaceCardListHeader
-                policyID={policyID}
-                cardSettings={cardSettings}
-            />
-        ),
-        [policyID, cardSettings],
-    );
+    const isSearchEmpty = filteredSortedCards.length === 0 && inputValue.length > 0;
 
-    const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle();
+    const renderListHeader = useCallback(() => <WorkspaceCardListHeader cardSettings={cardSettings} />, [cardSettings]);
 
-    const isSearchEmpty = filteredSortedCards.length === 0 && inputValue.length > 0;
+    const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle();
 
     return (
         <ScreenWrapper
@@ -194,14 +187,21 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
                 <EmptyCardView isBankAccountVerified={isBankAccountVerified} />
             ) : (
                 <>
-                    {sortedCards.length > 15 && (
-                        <SearchBar
-                            inputValue={inputValue}
-                            onChangeText={setInputValue}
-                            shouldShowEmptyState={isSearchEmpty}
-                            label={translate('workspace.expensifyCard.findCard')}
+                    <View style={styles.appBG}>
+                        <WorkspaceCardListLabels
+                            policyID={policyID}
+                            cardSettings={cardSettings}
                         />
-                    )}
+                        {sortedCards.length > 15 && (
+                            <SearchBar
+                                label={translate('workspace.expensifyCard.findCard')}
+                                inputValue={inputValue}
+                                onChangeText={setInputValue}
+                                shouldShowEmptyState={isSearchEmpty}
+                                style={[styles.mb0, styles.mt5]}
+                            />
+                        )}
+                    </View>
                     <FlatList
                         data={filteredSortedCards}
                         renderItem={renderItem}

From 76203ab0d6365053ecee01551ebe509c330e95cb Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Fri, 25 Apr 2025 14:11:47 +0700
Subject: [PATCH 10/15] fix: lint

---
 src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
index 092266a559c6..64d1675d3094 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
@@ -24,7 +24,7 @@ function WorkspaceCardListLabels({policyID, cardSettings}: WorkspaceCardListLabe
 
     const workspaceAccountID = useWorkspaceAccountID(policyID);
 
-    const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`);
+    const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`, {canBeMissing: true});
     const shouldShowSettlementButtonOrDate = !!cardSettings?.isMonthlySettlementAllowed || cardManualBilling;
 
     const isLessThanMediumScreen = isMediumScreenWidth || isSmallScreenWidth;

From d69d1546b5517e26e83fed58e78488e408fc815a Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Tue, 29 Apr 2025 03:32:17 +0700
Subject: [PATCH 11/15] fix: add search bar to per diem page

---
 src/languages/en.ts                           |  1 +
 src/languages/es.ts                           |  1 +
 .../perDiem/WorkspacePerDiemPage.tsx          | 35 +++++++++++++++----
 .../WorkspaceReportFieldsPage.tsx             |  5 ++-
 4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index bdbaf944f8bf..f0a4a0e6304c 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -3006,6 +3006,7 @@ const translations = {
                 other: 'Delete rates',
             }),
             deletePerDiemRate: 'Delete per diem rate',
+            findPerDiemRate: 'Find per diem rate',
             areYouSureDelete: () => ({
                 one: 'Are you sure you want to delete this rate?',
                 other: 'Are you sure you want to delete these rates?',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index c77a734e263b..485233898ab6 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -3033,6 +3033,7 @@ const translations = {
                 other: 'Eliminar tasas',
             }),
             deletePerDiemRate: 'Eliminar tasa per diem',
+            findPerDiemRate: 'Encontrar tasa per diem',
             areYouSureDelete: () => ({
                 one: '¿Estás seguro de que quieres eliminar esta tasa?',
                 other: '¿Estás seguro de que quieres eliminar estas tasas?',
diff --git a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
index b0b682f0a8a2..f7dc457ef9c2 100644
--- a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
+++ b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
@@ -15,6 +15,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
 import LottieAnimations from '@components/LottieAnimations';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import SearchBar from '@components/SearchBar';
 import TableListItem from '@components/SelectionList/TableListItem';
 import type {ListItem} from '@components/SelectionList/types';
 import SelectionListWithModal from '@components/SelectionListWithModal';
@@ -131,6 +132,7 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
     const policy = usePolicy(policyID);
     const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`);
     const {selectionMode} = useMobileSelectionMode();
+    const [inputValue, setInputValue] = useState('');
 
     const customUnit = getPerDiemCustomUnit(policy);
     const customUnitRates: Record<string, Rate> = useMemo(() => customUnit?.rates ?? {}, [customUnit]);
@@ -139,9 +141,6 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
 
     const allSubRates = getSubRatesData(allRatesArray);
 
-    // Filter out rates that will be deleted
-    const allSelectableSubRates = useMemo(() => allSubRates.filter((subRate) => subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [allSubRates]);
-
     const canSelectMultiple = shouldUseNarrowLayout ? selectionMode?.isEnabled : true;
 
     const fetchPerDiem = useCallback(() => {
@@ -196,6 +195,23 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
         [allSubRates, selectedPerDiem, canSelectMultiple, styles.flex2, styles.alignItemsStart, styles.textSupporting, styles.label, styles.pl2, styles.alignSelfEnd],
     );
 
+    const filteredSubRatesList = useMemo(() => {
+        if (!inputValue) {
+            return subRatesList;
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return subRatesList.filter((subRate) => subRate.text?.toLowerCase()?.includes(lowerQuery));
+    }, [inputValue, subRatesList]);
+
+    // Filter out rates that will be deleted
+    const allSelectableSubRates = useMemo(() => {
+        if (!inputValue) {
+            return allSubRates.filter((subRate) => subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+        }
+        const lowerQuery = inputValue.trim().toLowerCase();
+        return allSubRates.filter((subRate) => subRate.destination?.toLowerCase()?.includes(lowerQuery) && subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+    }, [inputValue, allSubRates]);
+
     const toggleSubRate = (subRate: PolicyOption) => {
         if (selectedPerDiem.find((selectedSubRate) => selectedSubRate.subRateID === subRate.subRateID) !== undefined) {
             setSelectedPerDiem((prev) => prev.filter((selectedSubRate) => selectedSubRate.subRateID !== subRate.subRateID));
@@ -408,7 +424,15 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
                     danger
                 />
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
-                {(!shouldUseNarrowLayout || !hasVisibleSubRates || isLoading) && getHeaderText()}
+                {(!hasVisibleSubRates || isLoading) && getHeaderText()}
+                {subRatesList.length > 15 && (
+                    <SearchBar
+                        label={translate('workspace.perDiem.findPerDiemRate')}
+                        inputValue={inputValue}
+                        onChangeText={setInputValue}
+                        shouldShowEmptyState={hasVisibleSubRates && !isLoading && filteredSubRatesList.length === 0}
+                    />
+                )}
                 {isLoading && (
                     <ActivityIndicator
                         size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -450,7 +474,7 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress
                         onTurnOnSelectionMode={(item) => item && toggleSubRate(item)}
-                        sections={[{data: subRatesList, isDisabled: false}]}
+                        sections={[{data: filteredSubRatesList, isDisabled: false}]}
                         onCheckboxPress={toggleSubRate}
                         onSelectRow={openSubRateDetails}
                         shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
@@ -458,7 +482,6 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
                         ListItem={TableListItem}
                         customListHeader={getCustomListHeader()}
                         listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                        listHeaderContent={shouldUseNarrowLayout ? getHeaderText() : null}
                         listItemTitleContainerStyles={styles.flex3}
                         showScrollIndicator={false}
                     />
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index 31b430c41d98..bba934f3ff68 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -162,7 +162,10 @@ function WorkspaceReportFieldsPage({
     };
 
     const toggleAllReportFields = () => {
-        const availableReportFields = Object.values(filteredPolicyFieldList).filter((reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
+        const availableReportFields = Object.values(filteredPolicyFieldList).filter(
+            (reportField) =>
+                reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFieldsSections.at(0)?.data.find((item) => item.fieldID === reportField.fieldID),
+        );
         setSelectedReportFields(selectedReportFields.length > 0 ? [] : availableReportFields);
     };
 

From c2c338353d4f2d99bd40a987c5c116f7480ea63c Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Wed, 30 Apr 2025 16:10:34 +0700
Subject: [PATCH 12/15] feat: add useTransition usage to pages

---
 src/components/SearchBar.tsx                  |  4 +-
 src/libs/CardUtils.ts                         |  9 +++
 src/pages/workspace/WorkspaceMembersPage.tsx  | 24 +++---
 .../categories/WorkspaceCategoriesPage.tsx    | 22 +++---
 .../WorkspaceCompanyCardsList.tsx             | 24 +++---
 .../distanceRates/PolicyDistanceRatesPage.tsx | 74 +++++++++---------
 .../WorkspaceExpensifyCardListPage.tsx        | 25 +++---
 .../perDiem/WorkspacePerDiemPage.tsx          | 19 +++--
 .../ReportFieldsListValuesPage.tsx            | 47 ++++++------
 .../WorkspaceReportFieldsPage.tsx             | 76 +++++++++----------
 .../workspace/tags/WorkspaceTagsPage.tsx      | 22 +++---
 .../workspace/tags/WorkspaceViewTagsPage.tsx  | 19 +++--
 .../workspace/taxes/WorkspaceTaxesPage.tsx    | 71 ++++++++---------
 13 files changed, 227 insertions(+), 209 deletions(-)

diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index 0f9a289e0f0a..e0a356c7f523 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -11,7 +11,7 @@ import Text from './Text';
 import TextInput from './TextInput';
 
 type SearchBarProps = {
-    label?: string;
+    label: string;
     icon?: IconAsset;
     inputValue: string;
     onChangeText?: (text: string) => void;
@@ -45,7 +45,7 @@ function SearchBar({label, style, icon = MagnifyingGlass, inputValue, onChangeTe
                 />
             </View>
             {!!shouldShowEmptyState && inputValue.length !== 0 && (
-                <View style={[styles.ph5, styles.pv3, styles.pb5]}>
+                <View style={[styles.ph5, styles.pt3, styles.pb5]}>
                     <Text style={[styles.textNormal, styles.colorMuted]}>{translate('common.noResultsFoundMatching', {searchString: inputValue})}</Text>
                 </View>
             )}
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 296d32fa1eac..8d4449924c7c 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -253,6 +253,14 @@ function sortCardsByCardholderName(cardsList: OnyxEntry<WorkspaceCardsList>, per
     });
 }
 
+function filterCards(searchQuery: string, card: Card, personalDetails?: PersonalDetailsList) {
+    const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
+    const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
+    const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
+    const accountName = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.displayName?.toLowerCase() ?? '';
+    return cardTitle.includes(searchQuery) || lastFourPAN.includes(searchQuery) || accountLogin.includes(searchQuery) || accountName.includes(searchQuery);
+}
+
 function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK, illustrations: IllustrationsType): IconAsset {
     const feedIcons = {
         [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: Illustrations.VisaCompanyCardDetailLarge,
@@ -619,4 +627,5 @@ export {
     isExpensifyCardFullySetUp,
     filterInactiveCards,
     getFundIdFromSettingsKey,
+    filterCards,
 };
diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index d2889fbf2ae6..f9529eabfefa 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -1,6 +1,6 @@
 import {useIsFocused} from '@react-navigation/native';
 import lodashIsEqual from 'lodash/isEqual';
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState, useTransition} from 'react';
 import type {TextInput} from 'react-native';
 import {InteractionManager, View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
@@ -380,8 +380,8 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
     const policyOwner = policy?.owner;
     const currentUserLogin = currentUserPersonalDetails.login;
     const invitedPrimaryToSecondaryLogins = invertObject(policy?.primaryLoginsInvited ?? {});
-    const getUsers = useCallback((): MemberOption[] => {
-        let result: MemberOption[] = [];
+    const data: MemberOption[] = useMemo(() => {
+        const result: MemberOption[] = [];
 
         Object.entries(policy?.employeeList ?? {}).forEach(([email, policyEmployee]) => {
             const accountID = Number(policyMemberEmailsToAccountIDs[email] ?? '');
@@ -443,7 +443,6 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
                 invitedSecondaryLogin: details?.login ? invitedPrimaryToSecondaryLogins[details.login] ?? '' : '',
             });
         });
-        result = sortAlphabetically(result, 'text');
         return result;
     }, [
         isOffline,
@@ -464,14 +463,17 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
         isPolicyAdmin,
     ]);
 
-    const data = useMemo(() => getUsers(), [getUsers]);
+    const [, startTransition] = useTransition();
+    const [filteredData, setFilteredData] = useState<MemberOption[]>([]);
 
-    const filteredData = useMemo(() => {
-        if (!inputValue.trim()) {
-            return data;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return data.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.alternateText?.toLowerCase().includes(lowerQuery));
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery
+                ? data.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.alternateText?.toLowerCase().includes(normalizedSearchQuery))
+                : data;
+            setFilteredData(sortAlphabetically(filtered, 'text'));
+        });
     }, [data, inputValue]);
 
     useEffect(() => {
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index f3df3c213d8e..fd0ea60d0e7b 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -1,5 +1,5 @@
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -52,7 +52,6 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
-import type {PolicyCategory} from '@src/types/onyx';
 import type DeepValueOf from '@src/types/utils/DeepValueOf';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
@@ -141,7 +140,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     );
 
     const categoryList = useMemo<PolicyOption[]>(() => {
-        const categories = lodashSortBy(Object.values(policyCategories ?? {}), 'name', localeCompare) as PolicyCategory[];
+        const categories = Object.values(policyCategories ?? {});
         return categories.reduce<PolicyOption[]>((acc, value) => {
             const isDisabled = value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
 
@@ -170,12 +169,17 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
         }, []);
     }, [policyCategories, isOffline, selectedCategories, canSelectMultiple, translate, updateWorkspaceRequiresCategory]);
 
-    const filteredCategoryList = useMemo(() => {
-        if (!inputValue.trim()) {
-            return categoryList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return categoryList.filter((cat) => cat.text?.toLowerCase().includes(lowerQuery));
+    const [, startTransition] = useTransition();
+    const [filteredCategoryList, setFilteredCategoryList] = useState<PolicyOption[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery
+                ? categoryList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.alternateText?.toLowerCase().includes(normalizedSearchQuery))
+                : categoryList;
+            setFilteredCategoryList(lodashSortBy(Object.values(filtered ?? {}), 'text', localeCompare) as PolicyOption[]);
+        });
     }, [inputValue, categoryList]);
 
     useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredCategoryList);
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
index 7f5af7c969cf..f9e94f482b45 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import type {ListRenderItemInfo} from 'react-native';
 import {FlatList, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
@@ -9,7 +9,7 @@ import SearchBar from '@components/SearchBar';
 import Text from '@components/Text';
 import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils';
+import {filterCards, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -41,17 +41,15 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
     const sortedCards = useMemo(() => sortCardsByCardholderName(cardsList, personalDetails), [cardsList, personalDetails]);
 
     const [inputValue, setInputValue] = useState('');
-    const filteredSortedCards = useMemo(() => {
-        if (!inputValue) {
-            return sortedCards;
-        }
-        const lowerValue = inputValue.toLowerCase();
-        return sortedCards.filter((card) => {
-            const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
-            const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
-            const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
-            const accountName = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.displayName?.toLowerCase() ?? '';
-            return cardTitle.includes(lowerValue) || lastFourPAN.includes(lowerValue) || accountLogin.includes(lowerValue) || accountName.includes(lowerValue);
+
+    const [, startTransition] = useTransition();
+    const [filteredSortedCards, setFilteredSortedCards] = useState<Card[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? sortedCards.filter((card) => filterCards(normalizedSearchQuery, card, personalDetails)) : sortedCards;
+            setFilteredSortedCards(filtered);
         });
     }, [inputValue, personalDetails, sortedCards]);
 
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index 3acf5aa77405..bd5d6200bd40 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -1,5 +1,5 @@
 import {useIsFocused} from '@react-navigation/native';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import Button from '@components/Button';
 import type {DropdownOption, WorkspaceDistanceRatesBulkActionType} from '@components/ButtonWithDropdownMenu/types';
@@ -45,7 +45,7 @@ import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {Rate} from '@src/types/onyx/Policy';
 
-type RateForList = ListItem & {value: string};
+type RateForList = ListItem & {value: string; rate?: number};
 
 type PolicyDistanceRatesPageProps = PlatformStackScreenProps<WorkspaceSplitNavigatorParamList, typeof SCREENS.WORKSPACE.DISTANCE_RATES>;
 
@@ -148,43 +148,45 @@ function PolicyDistanceRatesPage({
 
     const distanceRatesList = useMemo<RateForList[]>(
         () =>
-            Object.values(customUnitRates)
-                .sort((rateA, rateB) => (rateA?.rate ?? 0) - (rateB?.rate ?? 0))
-                .map((value) => ({
-                    value: value.customUnitRateID,
-                    text: `${convertAmountToDisplayString(value.rate, value.currency ?? CONST.CURRENCY.USD)} / ${translate(
-                        `common.${customUnit?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}`,
-                    )}`,
-                    keyForList: value.customUnitRateID,
-                    isSelected: selectedDistanceRates.find((rate) => rate.customUnitRateID === value.customUnitRateID) !== undefined && canSelectMultiple,
-                    isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
-                    pendingAction:
-                        value.pendingAction ??
-                        value.pendingFields?.rate ??
-                        value.pendingFields?.enabled ??
-                        value.pendingFields?.currency ??
-                        value.pendingFields?.taxRateExternalID ??
-                        value.pendingFields?.taxClaimablePercentage ??
-                        (policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD ? policy?.pendingAction : undefined),
-                    errors: value.errors ?? undefined,
-                    rightElement: (
-                        <Switch
-                            isOn={!!value?.enabled}
-                            accessibilityLabel={translate('workspace.distanceRates.trackTax')}
-                            onToggle={(newValue: boolean) => updateDistanceRateEnabled(newValue, value.customUnitRateID)}
-                            disabled={value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
-                        />
-                    ),
-                })),
+            Object.values(customUnitRates).map((value) => ({
+                rate: value.rate,
+                value: value.customUnitRateID,
+                text: `${convertAmountToDisplayString(value.rate, value.currency ?? CONST.CURRENCY.USD)} / ${translate(
+                    `common.${customUnit?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}`,
+                )}`,
+                keyForList: value.customUnitRateID,
+                isSelected: selectedDistanceRates.find((rate) => rate.customUnitRateID === value.customUnitRateID) !== undefined && canSelectMultiple,
+                isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
+                pendingAction:
+                    value.pendingAction ??
+                    value.pendingFields?.rate ??
+                    value.pendingFields?.enabled ??
+                    value.pendingFields?.currency ??
+                    value.pendingFields?.taxRateExternalID ??
+                    value.pendingFields?.taxClaimablePercentage ??
+                    (policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD ? policy?.pendingAction : undefined),
+                errors: value.errors ?? undefined,
+                rightElement: (
+                    <Switch
+                        isOn={!!value?.enabled}
+                        accessibilityLabel={translate('workspace.distanceRates.trackTax')}
+                        onToggle={(newValue: boolean) => updateDistanceRateEnabled(newValue, value.customUnitRateID)}
+                        disabled={value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
+                    />
+                ),
+            })),
         [customUnitRates, translate, customUnit, selectedDistanceRates, canSelectMultiple, policy?.pendingAction, updateDistanceRateEnabled],
     );
 
-    const filteredDistanceRatesList = useMemo<RateForList[]>(() => {
-        if (!inputValue.trim()) {
-            return distanceRatesList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return distanceRatesList.filter((rate) => rate.text?.toLowerCase().includes(lowerQuery));
+    const [, startTransition] = useTransition();
+    const [filteredDistanceRatesList, setFilteredDistanceRatesList] = useState<RateForList[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? distanceRatesList.filter((rate) => rate.text?.toLowerCase().includes(normalizedSearchQuery)) : distanceRatesList;
+            setFilteredDistanceRatesList(filtered.sort((rateA, rateB) => (rateA?.rate ?? 0) - (rateB?.rate ?? 0)));
+        });
     }, [distanceRatesList, inputValue]);
 
     const hasVisibleRates = useMemo(() => Object.values(customUnitRates).some((rate) => rate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [customUnitRates]);
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index b6617892b4d2..a2ed5e6fef90 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import type {ListRenderItemInfo} from 'react-native';
 import {FlatList, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
@@ -21,7 +21,7 @@ import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {clearDeletePaymentMethodError} from '@libs/actions/PaymentMethods';
-import {sortCardsByCardholderName} from '@libs/CardUtils';
+import {filterCards, sortCardsByCardholderName} from '@libs/CardUtils';
 import goBackFromWorkspaceCentralScreen from '@libs/Navigation/helpers/goBackFromWorkspaceCentralScreen';
 import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
@@ -77,19 +77,16 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
 
     const [inputValue, setInputValue] = useState('');
 
-    const filteredSortedCards = useMemo(() => {
-        if (!inputValue) {
-            return sortedCards;
-        }
-        const lowerValue = inputValue.toLowerCase();
-        return sortedCards.filter((card) => {
-            const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
-            const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
-            const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
-            const accountName = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.displayName?.toLowerCase() ?? '';
-            return cardTitle.includes(lowerValue) || lastFourPAN.includes(lowerValue) || accountLogin.includes(lowerValue) || accountName.includes(lowerValue);
+    const [, startTransition] = useTransition();
+    const [filteredSortedCards, setFilteredSortedCards] = useState<Card[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? sortedCards.filter((card) => filterCards(normalizedSearchQuery, card, personalDetails)) : sortedCards;
+            setFilteredSortedCards(filtered);
         });
-    }, [inputValue, sortedCards, personalDetails]);
+    }, [inputValue, personalDetails, sortedCards]);
 
     const handleIssueCardPress = () => {
         if (isActingAsDelegate) {
diff --git a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
index fb74a98036f1..21450dd04966 100644
--- a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
+++ b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
@@ -1,6 +1,6 @@
 import {useFocusEffect} from '@react-navigation/native';
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -160,7 +160,7 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
 
     const subRatesList = useMemo<PolicyOption[]>(
         () =>
-            (lodashSortBy(allSubRates, 'destination', localeCompare) as SubRateData[]).map((value) => {
+            allSubRates.map((value) => {
                 const isDisabled = value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
                 return {
                     text: value.destination,
@@ -195,12 +195,15 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
         [allSubRates, selectedPerDiem, canSelectMultiple, styles.flex2, styles.alignItemsStart, styles.textSupporting, styles.label, styles.pl2, styles.alignSelfEnd],
     );
 
-    const filteredSubRatesList = useMemo(() => {
-        if (!inputValue) {
-            return subRatesList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return subRatesList.filter((subRate) => subRate.text?.toLowerCase()?.includes(lowerQuery));
+    const [, startTransition] = useTransition();
+    const [filteredSubRatesList, setFilteredSubRatesList] = useState<PolicyOption[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? subRatesList.filter((subRate) => subRate.text?.toLowerCase()?.includes(normalizedSearchQuery)) : subRatesList;
+            setFilteredSubRatesList(lodashSortBy(filtered, 'text', localeCompare) as PolicyOption[]);
+        });
     }, [inputValue, subRatesList]);
 
     // Filter out rates that will be deleted
diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
index 401108963012..8496ebc55f8c 100644
--- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
+++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -119,9 +119,9 @@ function ReportFieldsListValuesPage({
         onNavigationCallBack: () => Navigation.goBack(),
     });
 
-    const listValuesSections = useMemo(() => {
-        const data = listValues
-            .map<ValueListItem>((value, index) => ({
+    const data = useMemo(
+        () =>
+            listValues.map<ValueListItem>((value, index) => ({
                 value,
                 index,
                 text: value,
@@ -135,25 +135,22 @@ function ReportFieldsListValuesPage({
                         onToggle={(newValue: boolean) => updateReportFieldListValueEnabled(newValue, index)}
                     />
                 ),
-            }))
-            .sort((a, b) => localeCompare(a.value, b.value));
-        return [{data, isDisabled: false}];
-    }, [canSelectMultiple, disabledListValues, listValues, selectedValues, translate, updateReportFieldListValueEnabled]);
-
-    const filteredListValuesSections = useMemo(() => {
-        if (!inputValue.trim()) {
-            return listValuesSections;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return [
-            {
-                data: listValuesSections.at(0)?.data.filter((reportField) => reportField.text?.toLowerCase().includes(lowerQuery)) ?? [],
-                isDisabled: listValuesSections?.at(0)?.isDisabled,
-            },
-        ];
-    }, [listValuesSections, inputValue]);
+            })),
+        [canSelectMultiple, disabledListValues, listValues, selectedValues, translate, updateReportFieldListValueEnabled],
+    );
+
+    const [, startTransition] = useTransition();
+    const [filteredListValuesSections, setFilteredListValuesSections] = useState<ValueListItem[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? data.filter((reportField) => reportField.text?.toLowerCase().includes(normalizedSearchQuery)) : data;
+            setFilteredListValuesSections(filtered.sort((a, b) => localeCompare(a.value, b.value)));
+        });
+    }, [data, inputValue]);
 
-    const filteredListValuesArray = (filteredListValuesSections.at(0)?.data ?? []).map((item) => item.value);
+    const filteredListValuesArray = filteredListValuesSections.map((item) => item.value);
 
     const shouldShowEmptyState = Object.values(listValues ?? {}).length <= 0;
     const selectedValuesArray = Object.keys(selectedValues).filter((key) => selectedValues[key]);
@@ -343,12 +340,12 @@ function ReportFieldsListValuesPage({
                 <View style={[styles.ph5, styles.pv4]}>
                     <Text style={[styles.sidebarLinkText, styles.optionAlternateText]}>{translate('workspace.reportFields.listInputSubtitle')}</Text>
                 </View>
-                {(listValuesSections.at(0)?.data?.length ?? 0) > 15 && (
+                {filteredListValuesSections.length > 15 && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
                         onChangeText={setInputValue}
-                        shouldShowEmptyState={!shouldShowEmptyState && filteredListValuesSections.at(0)?.data.length === 0}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredListValuesSections.length === 0}
                     />
                 )}
                 {shouldShowEmptyState && (
@@ -370,7 +367,7 @@ function ReportFieldsListValuesPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!hasAccountingConnections}
                         onTurnOnSelectionMode={(item) => item && toggleValue(item)}
-                        sections={filteredListValuesSections}
+                        sections={[{data: filteredListValuesSections, isDisabled: false}]}
                         onCheckboxPress={toggleValue}
                         onSelectRow={openListValuePage}
                         onSelectAll={toggleAllValues}
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index bba934f3ff68..acd5cde518e8 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -1,6 +1,6 @@
 import {useFocusEffect, useIsFocused} from '@react-navigation/native';
 import {Str} from 'expensify-common';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -113,45 +113,35 @@ function WorkspaceReportFieldsPage({
         setSelectedReportFields([]);
     }, [isFocused]);
 
-    const reportFieldsSections = useMemo(() => {
+    const data = useMemo(() => {
         if (!policy) {
-            return [{data: [], isDisabled: true}];
+            return [];
         }
+        return Object.values(filteredPolicyFieldList).map((reportField) => ({
+            value: reportField.name,
+            fieldID: reportField.fieldID,
+            keyForList: String(reportField.fieldID),
+            orderWeight: reportField.orderWeight,
+            pendingAction: reportField.pendingAction,
+            isSelected: selectedReportFields.find((selectedReportField) => selectedReportField.name === reportField.name) !== undefined && canSelectMultiple,
+            isDisabled: reportField.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
+            text: reportField.name,
+            rightElement: <ListItemRightCaretWithLabel labelText={Str.recapitalize(translate(getReportFieldTypeTranslationKey(reportField.type)))} />,
+        }));
+    }, [canSelectMultiple, filteredPolicyFieldList, policy, selectedReportFields, translate]);
 
-        return [
-            {
-                data: Object.values(filteredPolicyFieldList)
-                    .sort((a, b) => localeCompare(a.name, b.name))
-                    .map((reportField) => ({
-                        value: reportField.name,
-                        fieldID: reportField.fieldID,
-                        keyForList: String(reportField.fieldID),
-                        orderWeight: reportField.orderWeight,
-                        pendingAction: reportField.pendingAction,
-                        isSelected: selectedReportFields.find((selectedReportField) => selectedReportField.name === reportField.name) !== undefined && canSelectMultiple,
-                        isDisabled: reportField.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
-                        text: reportField.name,
-                        rightElement: <ListItemRightCaretWithLabel labelText={Str.recapitalize(translate(getReportFieldTypeTranslationKey(reportField.type)))} />,
-                    })),
-                isDisabled: false,
-            },
-        ];
-    }, [filteredPolicyFieldList, policy, selectedReportFields, canSelectMultiple, translate]);
+    const [, startTransition] = useTransition();
+    const [filteredReportFieldsSections, setFilteredReportFieldsSections] = useState<ReportFieldForList[]>([]);
 
-    const filteredReportFieldsSections = useMemo(() => {
-        if (!inputValue.trim()) {
-            return reportFieldsSections;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return [
-            {
-                data: reportFieldsSections.at(0)?.data.filter((reportField) => reportField.text.toLowerCase().includes(lowerQuery)) ?? [],
-                isDisabled: reportFieldsSections?.at(0)?.isDisabled,
-            },
-        ];
-    }, [reportFieldsSections, inputValue]);
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? data.filter((reportField) => reportField.text.toLowerCase().includes(normalizedSearchQuery)) : data;
+            setFilteredReportFieldsSections(filtered.sort((a, b) => localeCompare(a.value, b.value)));
+        });
+    }, [inputValue, data]);
 
-    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredReportFieldsSections.at(0)?.data ?? ([] as ListItem[]));
+    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredReportFieldsSections);
 
     const updateSelectedReportFields = (item: ReportFieldForList) => {
         const fieldKey = getReportFieldKey(item.fieldID);
@@ -163,8 +153,7 @@ function WorkspaceReportFieldsPage({
 
     const toggleAllReportFields = () => {
         const availableReportFields = Object.values(filteredPolicyFieldList).filter(
-            (reportField) =>
-                reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFieldsSections.at(0)?.data.find((item) => item.fieldID === reportField.fieldID),
+            (reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFieldsSections.find((item) => item.fieldID === reportField.fieldID),
         );
         setSelectedReportFields(selectedReportFields.length > 0 ? [] : availableReportFields);
     };
@@ -294,12 +283,12 @@ function WorkspaceReportFieldsPage({
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleReportField || isLoading) && getHeaderText()}
                 {!shouldShowEmptyState && !isLoading && shouldUseNarrowLayout && getHeaderText()}
-                {(reportFieldsSections.at(0)?.data?.length ?? 0) > 15 && (
+                {filteredReportFieldsSections.length > 15 && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
                         onChangeText={setInputValue}
-                        shouldShowEmptyState={!shouldShowEmptyState && filteredReportFieldsSections.at(0)?.data.length === 0}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredReportFieldsSections.length === 0}
                     />
                 )}
                 {isLoading && (
@@ -309,7 +298,7 @@ function WorkspaceReportFieldsPage({
                         color={theme.spinner}
                     />
                 )}
-                {shouldShowEmptyState && filteredReportFieldsSections.at(0)?.data.length === 0 && (
+                {shouldShowEmptyState && filteredReportFieldsSections.length === 0 && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
                             title={translate('workspace.reportFields.emptyReportFields.title')}
@@ -328,7 +317,12 @@ function WorkspaceReportFieldsPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!hasReportAccountingConnections}
                         onTurnOnSelectionMode={(item) => item && updateSelectedReportFields(item)}
-                        sections={filteredReportFieldsSections}
+                        sections={[
+                            {
+                                data: filteredReportFieldsSections,
+                                isDisabled: !policy,
+                            },
+                        ]}
                         onCheckboxPress={updateSelectedReportFields}
                         onSelectRow={navigateToReportFieldsSettings}
                         onSelectAll={toggleAllReportFields}
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index 6a93ca1da2ad..53feeb6173c3 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -1,5 +1,5 @@
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -190,8 +190,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
                 };
             });
         }
-        const sortedTags = lodashSortBy(Object.values(policyTagLists.at(0)?.tags ?? {}), 'name', localeCompare) as PolicyTag[];
-        return sortedTags.map((tag) => ({
+        return Object.values(policyTagLists?.at(0)?.tags ?? {}).map((tag) => ({
             value: tag.name,
             text: getCleanedTagName(tag.name),
             keyForList: tag.name,
@@ -211,12 +210,17 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
         }));
     }, [isMultiLevelTags, policyTagLists, selectedTags, canSelectMultiple, translate, updateWorkspaceRequiresTag, updateWorkspaceTagEnabled]);
 
-    const filteredTagList = useMemo<TagListItem[]>(() => {
-        if (!inputValue.trim()) {
-            return tagList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return tagList.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.value?.toLowerCase().includes(lowerQuery));
+    const [, startTransition] = useTransition();
+    const [filteredTagList, setFilteredTagList] = useState<TagListItem[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery
+                ? tagList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.value?.toLowerCase().includes(normalizedSearchQuery))
+                : tagList;
+            setFilteredTagList(lodashSortBy(filtered, 'value', localeCompare) as TagListItem[]);
+        });
     }, [tagList, inputValue]);
 
     const filteredTagListKeyedByName = useMemo(
diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
index 7958d85454d0..59c7a02739ba 100644
--- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
@@ -1,5 +1,5 @@
 import {useIsFocused} from '@react-navigation/native';
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
@@ -130,12 +130,17 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
         [currentPolicyTag?.tags, selectedTags, canSelectMultiple, translate, updateWorkspaceTagEnabled],
     );
 
-    const filteredTagList = useMemo(() => {
-        if (!inputValue.trim()) {
-            return tagList;
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return tagList.filter((item) => !!item.text?.toLowerCase().includes(lowerQuery) || !!item.value?.toLowerCase().includes(lowerQuery));
+    const [, startTransition] = useTransition();
+    const [filteredTagList, setFilteredTagList] = useState<TagListItem[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery
+                ? tagList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.value?.toLowerCase().includes(normalizedSearchQuery))
+                : tagList;
+            setFilteredTagList(filtered.sort((tagA, tagB) => localeCompare(tagA.value, tagB.value)));
+        });
     }, [tagList, inputValue]);
 
     const hasDependentTags = useMemo(() => hasDependentTagsPolicyUtils(policy, policyTags), [policy, policyTags]);
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index 1734c53e7e59..2c5f79efcfe9 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -159,41 +159,44 @@ function WorkspaceTaxesPage({
         if (!policy) {
             return [];
         }
-        return Object.entries(policy.taxRates?.taxes ?? {})
-            .map(([key, value]) => {
-                const canEditTaxRate = policy && canEditTaxRatePolicyUtils(policy, key);
-
-                return {
-                    text: value.name,
-                    alternateText: textForDefault(key, value),
-                    keyForList: key,
-                    isSelected: !!selectedTaxesIDs.includes(key) && canSelectMultiple,
-                    isDisabledCheckbox: !canEditTaxRatePolicyUtils(policy, key),
-                    isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
-                    pendingAction: value.pendingAction ?? (Object.keys(value.pendingFields ?? {}).length > 0 ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : null),
-                    errors: value.errors ?? getLatestErrorFieldForAnyField(value),
-                    rightElement: (
-                        <Switch
-                            isOn={!value.isDisabled}
-                            disabled={!canEditTaxRate || value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
-                            accessibilityLabel={translate('workspace.taxes.actions.enable')}
-                            onToggle={(newValue: boolean) => updateWorkspaceTaxEnabled(newValue, key)}
-                        />
-                    ),
-                };
-            })
-            .sort((a, b) => (a.text ?? a.keyForList ?? '').localeCompare(b.text ?? b.keyForList ?? ''));
+        return Object.entries(policy.taxRates?.taxes ?? {}).map(([key, value]) => {
+            const canEditTaxRate = policy && canEditTaxRatePolicyUtils(policy, key);
+
+            return {
+                text: value.name,
+                alternateText: textForDefault(key, value),
+                keyForList: key,
+                isSelected: !!selectedTaxesIDs.includes(key) && canSelectMultiple,
+                isDisabledCheckbox: !canEditTaxRatePolicyUtils(policy, key),
+                isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
+                pendingAction: value.pendingAction ?? (Object.keys(value.pendingFields ?? {}).length > 0 ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : null),
+                errors: value.errors ?? getLatestErrorFieldForAnyField(value),
+                rightElement: (
+                    <Switch
+                        isOn={!value.isDisabled}
+                        disabled={!canEditTaxRate || value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
+                        accessibilityLabel={translate('workspace.taxes.actions.enable')}
+                        onToggle={(newValue: boolean) => updateWorkspaceTaxEnabled(newValue, key)}
+                    />
+                ),
+            };
+        });
     }, [policy, textForDefault, selectedTaxesIDs, canSelectMultiple, translate, updateWorkspaceTaxEnabled]);
 
-    const filteredTaxesList = useMemo(() => {
-        if (!inputValue) {
-            return taxesList;
-        }
-        const lowerQuery = inputValue.toLowerCase();
-        return taxesList.filter((tax) => {
-            const taxName = tax.text?.toLowerCase() ?? '';
-            const taxAlternateText = tax.alternateText?.toLowerCase() ?? '';
-            return taxName.includes(lowerQuery) || taxAlternateText.includes(lowerQuery);
+    const [, startTransition] = useTransition();
+    const [filteredTaxesList, setFilteredTaxesList] = useState<ListItem[]>([]);
+
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery
+                ? taxesList.filter((tax) => {
+                      const taxName = tax.text?.toLowerCase() ?? '';
+                      const taxAlternateText = tax.alternateText?.toLowerCase() ?? '';
+                      return taxName.includes(normalizedSearchQuery) || taxAlternateText.includes(normalizedSearchQuery);
+                  })
+                : taxesList;
+            setFilteredTaxesList(filtered.sort((a, b) => (a.text ?? a.keyForList ?? '').localeCompare(b.text ?? b.keyForList ?? '')));
         });
     }, [inputValue, taxesList]);
 

From 36290414ca3a03d9f48f420307b7e064debd4676 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Sat, 3 May 2025 00:50:25 +0700
Subject: [PATCH 13/15] fix: migrate useTransition to a new hook

---
 src/hooks/useSearchResults.ts                 | 23 +++++++++++++
 src/libs/CardUtils.ts                         | 27 ++++++++--------
 src/pages/workspace/WorkspaceMembersPage.tsx  | 22 +++++--------
 .../categories/WorkspaceCategoriesPage.tsx    | 23 ++++++-------
 .../WorkspaceCompanyCardsList.tsx             | 32 +++++++------------
 .../distanceRates/PolicyDistanceRatesPage.tsx | 21 ++++--------
 .../expensifyCard/WorkspaceCardListLabels.tsx |  2 +-
 .../WorkspaceExpensifyCardListPage.tsx        | 30 +++++++----------
 .../perDiem/WorkspacePerDiemPage.tsx          | 32 ++++++-------------
 .../ReportFieldsListValuesPage.tsx            | 26 ++++++---------
 .../WorkspaceReportFieldsPage.tsx             | 30 +++++++----------
 .../workspace/tags/WorkspaceTagsPage.tsx      | 20 +++---------
 .../workspace/tags/WorkspaceViewTagsPage.tsx  | 19 +++--------
 .../workspace/taxes/WorkspaceTaxesPage.tsx    | 31 ++++++++----------
 14 files changed, 138 insertions(+), 200 deletions(-)
 create mode 100644 src/hooks/useSearchResults.ts

diff --git a/src/hooks/useSearchResults.ts b/src/hooks/useSearchResults.ts
new file mode 100644
index 000000000000..5bd5d85cd100
--- /dev/null
+++ b/src/hooks/useSearchResults.ts
@@ -0,0 +1,23 @@
+import {useEffect, useState, useTransition} from 'react';
+
+/**
+ * This hook filters (and optionally sorts) a dataset based on a search parameter.
+ * It utilizes `useTransition` to allow the searchQuery to change rapidly, while more expensive renders that occur using
+ * the result of the filtering and sorting are deprioritized, allowing them to happen in the background.
+ */
+function useSearchResults<TValue>(data: TValue[], filterData: (datum: TValue, searchInput: string) => boolean, sortData: (data: TValue[]) => TValue[] = (d) => d) {
+    const [inputValue, setInputValue] = useState('');
+    const [result, setResult] = useState(data);
+    const [, startTransition] = useTransition();
+    useEffect(() => {
+        startTransition(() => {
+            const normalizedSearchQuery = inputValue.trim().toLowerCase();
+            const filtered = normalizedSearchQuery ? data.filter((item) => filterData(item, inputValue)) : data;
+            const sorted = sortData(filtered);
+            setResult(sorted);
+        });
+    }, [data, filterData, inputValue, sortData]);
+    return [inputValue, setInputValue, result] as const;
+}
+
+export default useSearchResults;
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 8d5ed7d6ad85..b24531ef2050 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -240,22 +240,22 @@ function getEligibleBankAccountsForCard(bankAccountsList: OnyxEntry<BankAccountL
     return Object.values(bankAccountsList).filter((bankAccount) => bankAccount?.accountData?.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS && bankAccount?.accountData?.allowDebit);
 }
 
-function sortCardsByCardholderName(cardsList: OnyxEntry<WorkspaceCardsList>, personalDetails: OnyxEntry<PersonalDetailsList>, policyMembersAccountIDs: number[]): Card[] {
+function getCardsByCardholderName(cardsList: OnyxEntry<WorkspaceCardsList>, policyMembersAccountIDs: number[]): Card[] {
     const {cardList, ...cards} = cardsList ?? {};
-    return Object.values(cards)
-        .filter((card: Card) => card.accountID && policyMembersAccountIDs.includes(card.accountID))
-        .sort((cardA: Card, cardB: Card) => {
-            const userA = cardA.accountID ? personalDetails?.[cardA.accountID] ?? {} : {};
-            const userB = cardB.accountID ? personalDetails?.[cardB.accountID] ?? {} : {};
-
-            const aName = getDisplayNameOrDefault(userA);
-            const bName = getDisplayNameOrDefault(userB);
+    return Object.values(cards).filter((card: Card) => card.accountID && policyMembersAccountIDs.includes(card.accountID));
+}
 
-            return localeCompare(aName, bName);
-        });
+function sortCardsByCardholderName(cards: Card[], personalDetails: OnyxEntry<PersonalDetailsList>): Card[] {
+    return cards.sort((cardA: Card, cardB: Card) => {
+        const userA = cardA.accountID ? personalDetails?.[cardA.accountID] ?? {} : {};
+        const userB = cardB.accountID ? personalDetails?.[cardB.accountID] ?? {} : {};
+        const aName = getDisplayNameOrDefault(userA);
+        const bName = getDisplayNameOrDefault(userB);
+        return localeCompare(aName, bName);
+    });
 }
 
-function filterCards(searchQuery: string, card: Card, personalDetails?: PersonalDetailsList) {
+function filterCardsByPersonalDetails(card: Card, searchQuery: string, personalDetails?: PersonalDetailsList) {
     const cardTitle = card.nameValuePairs?.cardTitle?.toLowerCase() ?? '';
     const lastFourPAN = card?.lastFourPAN?.toLowerCase() ?? '';
     const accountLogin = personalDetails?.[card.accountID ?? CONST.DEFAULT_NUMBER_ID]?.login?.toLowerCase() ?? '';
@@ -629,5 +629,6 @@ export {
     isExpensifyCardFullySetUp,
     filterInactiveCards,
     getFundIdFromSettingsKey,
-    filterCards,
+    getCardsByCardholderName,
+    filterCardsByPersonalDetails,
 };
diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index f9529eabfefa..964999ecdb21 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -1,6 +1,6 @@
 import {useIsFocused} from '@react-navigation/native';
 import lodashIsEqual from 'lodash/isEqual';
-import React, {useCallback, useEffect, useMemo, useRef, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
 import type {TextInput} from 'react-native';
 import {InteractionManager, View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
@@ -29,6 +29,7 @@ import useNetwork from '@hooks/useNetwork';
 import usePrevious from '@hooks/usePrevious';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useThreeDotsAnchorPosition from '@hooks/useThreeDotsAnchorPosition';
 import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
@@ -100,7 +101,6 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
     const isOfflineAndNoMemberDataAvailable = isEmptyObject(policy?.employeeList) && isOffline;
     const prevPersonalDetails = usePrevious(personalDetails);
     const {translate, formatPhoneNumber, preferredLocale} = useLocalize();
-    const [inputValue, setInputValue] = useState('');
 
     // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type for the decision modal
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
@@ -463,18 +463,12 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
         isPolicyAdmin,
     ]);
 
-    const [, startTransition] = useTransition();
-    const [filteredData, setFilteredData] = useState<MemberOption[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery
-                ? data.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.alternateText?.toLowerCase().includes(normalizedSearchQuery))
-                : data;
-            setFilteredData(sortAlphabetically(filtered, 'text'));
-        });
-    }, [data, inputValue]);
+    const filterMember = useCallback(
+        (memberOption: MemberOption, searchQuery: string) => !!memberOption.text?.toLowerCase().includes(searchQuery) || !!memberOption.alternateText?.toLowerCase().includes(searchQuery),
+        [],
+    );
+    const sortMembers = useCallback((memberOptions: MemberOption[]) => sortAlphabetically(memberOptions, 'text'), []);
+    const [inputValue, setInputValue, filteredData] = useSearchResults(data, filterMember, sortMembers);
 
     useEffect(() => {
         if (!isFocused) {
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index fd0ea60d0e7b..fec2bfd7e61e 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -1,5 +1,5 @@
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -33,6 +33,7 @@ import usePermissions from '@hooks/usePermissions';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useThreeDotsAnchorPosition from '@hooks/useThreeDotsAnchorPosition';
@@ -87,7 +88,6 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
     const isConnectionVerified = connectedIntegration && !isConnectionUnverified(policy, connectedIntegration);
     const currentConnectionName = getCurrentConnectionName(policy);
     const isQuickSettingsFlow = !!backTo;
-    const [inputValue, setInputValue] = useState('');
     const {canUseLeftHandBar} = usePermissions();
 
     const canSelectMultiple = isSmallScreenWidth ? selectionMode?.isEnabled : true;
@@ -169,18 +169,13 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
         }, []);
     }, [policyCategories, isOffline, selectedCategories, canSelectMultiple, translate, updateWorkspaceRequiresCategory]);
 
-    const [, startTransition] = useTransition();
-    const [filteredCategoryList, setFilteredCategoryList] = useState<PolicyOption[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery
-                ? categoryList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.alternateText?.toLowerCase().includes(normalizedSearchQuery))
-                : categoryList;
-            setFilteredCategoryList(lodashSortBy(Object.values(filtered ?? {}), 'text', localeCompare) as PolicyOption[]);
-        });
-    }, [inputValue, categoryList]);
+    const filterCategory = useCallback((categoryOption: PolicyOption, searchInput: string) => {
+        return !!categoryOption.text?.toLowerCase().includes(searchInput) || !!categoryOption.alternateText?.toLowerCase().includes(searchInput);
+    }, []);
+    const sortCategories = useCallback((data: PolicyOption[]) => {
+        return lodashSortBy(data, 'text', localeCompare) as PolicyOption[];
+    }, []);
+    const [inputValue, setInputValue, filteredCategoryList] = useSearchResults(categoryList, filterCategory, sortCategories);
 
     useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredCategoryList);
 
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
index a1f114a5e0eb..639ae4e73fc7 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useMemo} from 'react';
 import type {ListRenderItemInfo} from 'react-native';
 import {FlatList, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
@@ -9,8 +9,9 @@ import SearchBar from '@components/SearchBar';
 import Text from '@components/Text';
 import useLocalize from '@hooks/useLocalize';
 import usePolicy from '@hooks/usePolicy';
+import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {filterCards, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils';
+import {filterCardsByPersonalDetails, getCardsByCardholderName, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils';
 import {getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
@@ -41,23 +42,14 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
     const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true});
     const policy = usePolicy(policyID);
 
-    const sortedCards = useMemo(
-        () => sortCardsByCardholderName(cardsList, personalDetails, Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList))),
-        [cardsList, personalDetails, policy?.employeeList],
-    );
-
-    const [inputValue, setInputValue] = useState('');
-
-    const [, startTransition] = useTransition();
-    const [filteredSortedCards, setFilteredSortedCards] = useState<Card[]>([]);
+    const allCards = useMemo(() => {
+        const policyMembersAccountIDs = Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList));
+        return getCardsByCardholderName(cardsList, policyMembersAccountIDs);
+    }, [cardsList, policy?.employeeList]);
 
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? sortedCards.filter((card) => filterCards(normalizedSearchQuery, card, personalDetails)) : sortedCards;
-            setFilteredSortedCards(filtered);
-        });
-    }, [inputValue, personalDetails, sortedCards]);
+    const filterCard = useCallback((card: Card, searchInput: string) => filterCardsByPersonalDetails(card, searchInput, personalDetails), [personalDetails]);
+    const sortCards = useCallback((cards: Card[]) => sortCardsByCardholderName(cards, personalDetails), [personalDetails]);
+    const [inputValue, setInputValue, filteredSortedCards] = useSearchResults(allCards, filterCard, sortCards);
 
     const renderItem = useCallback(
         ({item, index}: ListRenderItemInfo<Card>) => {
@@ -115,7 +107,7 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
         [styles, translate],
     );
 
-    if (sortedCards.length === 0) {
+    if (allCards.length === 0) {
         return (
             <WorkspaceCompanyCardsFeedAddedEmptyPage
                 handleAssignCard={handleAssignCard}
@@ -128,7 +120,7 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis
 
     return (
         <>
-            {sortedCards.length > 0 && (
+            {allCards.length > 0 && (
                 <SearchBar
                     label={translate('workspace.companyCards.findCard')}
                     inputValue={inputValue}
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index bd5d6200bd40..5caea74c380f 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -1,5 +1,5 @@
 import {useIsFocused} from '@react-navigation/native';
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import Button from '@components/Button';
 import type {DropdownOption, WorkspaceDistanceRatesBulkActionType} from '@components/ButtonWithDropdownMenu/types';
@@ -21,6 +21,7 @@ import useNetwork from '@hooks/useNetwork';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
@@ -64,7 +65,6 @@ function PolicyDistanceRatesPage({
     const isFocused = useIsFocused();
     const policy = usePolicy(policyID);
     const {selectionMode} = useMobileSelectionMode();
-    const [inputValue, setInputValue] = useState('');
 
     const canSelectMultiple = shouldUseNarrowLayout ? selectionMode?.isEnabled : true;
 
@@ -178,18 +178,9 @@ function PolicyDistanceRatesPage({
         [customUnitRates, translate, customUnit, selectedDistanceRates, canSelectMultiple, policy?.pendingAction, updateDistanceRateEnabled],
     );
 
-    const [, startTransition] = useTransition();
-    const [filteredDistanceRatesList, setFilteredDistanceRatesList] = useState<RateForList[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? distanceRatesList.filter((rate) => rate.text?.toLowerCase().includes(normalizedSearchQuery)) : distanceRatesList;
-            setFilteredDistanceRatesList(filtered.sort((rateA, rateB) => (rateA?.rate ?? 0) - (rateB?.rate ?? 0)));
-        });
-    }, [distanceRatesList, inputValue]);
-
-    const hasVisibleRates = useMemo(() => Object.values(customUnitRates).some((rate) => rate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), [customUnitRates]);
+    const filterRate = useCallback((rate: RateForList, searchInput: string) => !!rate.text?.toLowerCase().includes(searchInput.toLowerCase()), []);
+    const sortRates = useCallback((rates: RateForList[]) => rates.sort((a, b) => (a.rate ?? 0) - (b.rate ?? 0)), []);
+    const [inputValue, setInputValue, filteredDistanceRatesList] = useSearchResults(distanceRatesList, filterRate, sortRates);
 
     const addRate = () => {
         Navigation.navigate(ROUTES.WORKSPACE_CREATE_DISTANCE_RATE.getRoute(policyID));
@@ -383,7 +374,7 @@ function PolicyDistanceRatesPage({
                         label={translate('workspace.distanceRates.findRate')}
                         inputValue={inputValue}
                         onChangeText={setInputValue}
-                        shouldShowEmptyState={hasVisibleRates && filteredDistanceRatesList.length === 0}
+                        shouldShowEmptyState={filteredDistanceRatesList.length === 0}
                     />
                 )}
                 {isLoading && (
diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
index 64d1675d3094..41171a4da0b6 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx
@@ -52,7 +52,7 @@ function WorkspaceCardListLabels({policyID, cardSettings}: WorkspaceCardListLabe
                 type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE}
                 value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE] ?? 0}
             />
-            <View style={[styles.flexRow, !isLessThanMediumScreen && styles.flex2, isLessThanMediumScreen && styles.mt5]}>
+            <View style={[styles.flexRow, !isLessThanMediumScreen ? styles.flex2 : styles.mt5]}>
                 <WorkspaceCardsListLabel
                     type={CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT}
                     value={cardSettings?.[CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.REMAINING_LIMIT] ?? 0}
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index 41750ea71a82..19eb1969fd79 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
 import type {ListRenderItemInfo} from 'react-native';
 import {FlatList, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
@@ -19,9 +19,10 @@ import useExpensifyCardFeeds from '@hooks/useExpensifyCardFeeds';
 import useLocalize from '@hooks/useLocalize';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {clearDeletePaymentMethodError} from '@libs/actions/PaymentMethods';
-import {filterCards, sortCardsByCardholderName} from '@libs/CardUtils';
+import {filterCardsByPersonalDetails, getCardsByCardholderName, sortCardsByCardholderName} from '@libs/CardUtils';
 import goBackFromWorkspaceCentralScreen from '@libs/Navigation/helpers/goBackFromWorkspaceCentralScreen';
 import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
 import {getDescriptionForPolicyDomainCard, getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils';
@@ -73,23 +74,14 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
 
     const policyCurrency = useMemo(() => policy?.outputCurrency ?? CONST.CURRENCY.USD, [policy]);
 
-    const sortedCards = useMemo(
-        () => sortCardsByCardholderName(cardsList, personalDetails, Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList))),
-        [cardsList, personalDetails, policy?.employeeList],
-    );
-
-    const [inputValue, setInputValue] = useState('');
-
-    const [, startTransition] = useTransition();
-    const [filteredSortedCards, setFilteredSortedCards] = useState<Card[]>([]);
+    const allCards = useMemo(() => {
+        const policyMembersAccountIDs = Object.values(getMemberAccountIDsForWorkspace(policy?.employeeList));
+        return getCardsByCardholderName(cardsList, policyMembersAccountIDs);
+    }, [cardsList, policy?.employeeList]);
 
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? sortedCards.filter((card) => filterCards(normalizedSearchQuery, card, personalDetails)) : sortedCards;
-            setFilteredSortedCards(filtered);
-        });
-    }, [inputValue, personalDetails, sortedCards]);
+    const filterCard = useCallback((card: Card, searchInput: string) => filterCardsByPersonalDetails(card, searchInput, personalDetails), [personalDetails]);
+    const sortCards = useCallback((cards: Card[]) => sortCardsByCardholderName(cards, personalDetails), [personalDetails]);
+    const [inputValue, setInputValue, filteredSortedCards] = useSearchResults(allCards, filterCard, sortCards);
 
     const handleIssueCardPress = () => {
         if (isActingAsDelegate) {
@@ -192,7 +184,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
                             policyID={policyID}
                             cardSettings={cardSettings}
                         />
-                        {sortedCards.length > 15 && (
+                        {allCards.length > 15 && (
                             <SearchBar
                                 label={translate('workspace.expensifyCard.findCard')}
                                 inputValue={inputValue}
diff --git a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
index 21450dd04966..9834b33d3a38 100644
--- a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
+++ b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
@@ -1,6 +1,6 @@
 import {useFocusEffect} from '@react-navigation/native';
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -29,6 +29,7 @@ import useNetwork from '@hooks/useNetwork';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useThreeDotsAnchorPosition from '@hooks/useThreeDotsAnchorPosition';
@@ -132,7 +133,6 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
     const policy = usePolicy(policyID);
     const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: false});
     const {selectionMode} = useMobileSelectionMode();
-    const [inputValue, setInputValue] = useState('');
 
     const customUnit = getPerDiemCustomUnit(policy);
     const customUnitRates: Record<string, Rate> = useMemo(() => customUnit?.rates ?? {}, [customUnit]);
@@ -195,25 +195,9 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
         [allSubRates, selectedPerDiem, canSelectMultiple, styles.flex2, styles.alignItemsStart, styles.textSupporting, styles.label, styles.pl2, styles.alignSelfEnd],
     );
 
-    const [, startTransition] = useTransition();
-    const [filteredSubRatesList, setFilteredSubRatesList] = useState<PolicyOption[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? subRatesList.filter((subRate) => subRate.text?.toLowerCase()?.includes(normalizedSearchQuery)) : subRatesList;
-            setFilteredSubRatesList(lodashSortBy(filtered, 'text', localeCompare) as PolicyOption[]);
-        });
-    }, [inputValue, subRatesList]);
-
-    // Filter out rates that will be deleted
-    const allSelectableSubRates = useMemo(() => {
-        if (!inputValue) {
-            return allSubRates.filter((subRate) => subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
-        }
-        const lowerQuery = inputValue.trim().toLowerCase();
-        return allSubRates.filter((subRate) => subRate.destination?.toLowerCase()?.includes(lowerQuery) && subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
-    }, [inputValue, allSubRates]);
+    const filterRate = useCallback((rate: PolicyOption, searchInput: string) => !!rate.text?.toLowerCase().includes(searchInput), []);
+    const sortRates = useCallback((rates: PolicyOption[]) => lodashSortBy(rates, 'text', localeCompare) as PolicyOption[], []);
+    const [inputValue, setInputValue, filteredSubRatesList] = useSearchResults(subRatesList, filterRate, sortRates);
 
     const toggleSubRate = (subRate: PolicyOption) => {
         if (selectedPerDiem.find((selectedSubRate) => selectedSubRate.subRateID === subRate.subRateID) !== undefined) {
@@ -231,7 +215,11 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
         if (selectedPerDiem.length > 0) {
             setSelectedPerDiem([]);
         } else {
-            setSelectedPerDiem([...allSelectableSubRates]);
+            const availablePerDiemRates = allSubRates.filter(
+                (subRate) =>
+                    subRate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredSubRatesList.some((filteredSubRate) => filteredSubRate.subRateID === subRate.subRateID),
+            );
+            setSelectedPerDiem(availablePerDiemRates);
         }
     };
 
diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
index 8496ebc55f8c..ebc4f847a4e4 100644
--- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
+++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
 import {View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -23,6 +23,7 @@ import useLocalize from '@hooks/useLocalize';
 import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
 import {
@@ -98,8 +99,6 @@ function ReportFieldsListValuesPage({
         return [reportFieldValues, reportFieldDisabledValues];
     }, [formDraft?.disabledListValues, formDraft?.listValues, policy?.fieldList, reportFieldID]);
 
-    const [inputValue, setInputValue] = useState('');
-
     const updateReportFieldListValueEnabled = useCallback(
         (value: boolean, valueIndex: number) => {
             if (reportFieldID) {
@@ -139,18 +138,11 @@ function ReportFieldsListValuesPage({
         [canSelectMultiple, disabledListValues, listValues, selectedValues, translate, updateReportFieldListValueEnabled],
     );
 
-    const [, startTransition] = useTransition();
-    const [filteredListValuesSections, setFilteredListValuesSections] = useState<ValueListItem[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? data.filter((reportField) => reportField.text?.toLowerCase().includes(normalizedSearchQuery)) : data;
-            setFilteredListValuesSections(filtered.sort((a, b) => localeCompare(a.value, b.value)));
-        });
-    }, [data, inputValue]);
+    const filterListValue = useCallback((item: ValueListItem, searchInput: string) => !!item.text?.toLowerCase().includes(searchInput.toLowerCase()), []);
+    const sortListValues = useCallback((values: ValueListItem[]) => values.sort((a, b) => localeCompare(a.value, b.value)), []);
+    const [inputValue, setInputValue, filteredListValues] = useSearchResults(data, filterListValue, sortListValues);
 
-    const filteredListValuesArray = filteredListValuesSections.map((item) => item.value);
+    const filteredListValuesArray = filteredListValues.map((item) => item.value);
 
     const shouldShowEmptyState = Object.values(listValues ?? {}).length <= 0;
     const selectedValuesArray = Object.keys(selectedValues).filter((key) => selectedValues[key]);
@@ -340,12 +332,12 @@ function ReportFieldsListValuesPage({
                 <View style={[styles.ph5, styles.pv4]}>
                     <Text style={[styles.sidebarLinkText, styles.optionAlternateText]}>{translate('workspace.reportFields.listInputSubtitle')}</Text>
                 </View>
-                {filteredListValuesSections.length > 15 && (
+                {filteredListValues.length > 15 && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
                         onChangeText={setInputValue}
-                        shouldShowEmptyState={!shouldShowEmptyState && filteredListValuesSections.length === 0}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredListValues.length === 0}
                     />
                 )}
                 {shouldShowEmptyState && (
@@ -367,7 +359,7 @@ function ReportFieldsListValuesPage({
                         canSelectMultiple={canSelectMultiple}
                         turnOnSelectionModeOnLongPress={!hasAccountingConnections}
                         onTurnOnSelectionMode={(item) => item && toggleValue(item)}
-                        sections={[{data: filteredListValuesSections, isDisabled: false}]}
+                        sections={[{data: filteredListValues, isDisabled: false}]}
                         onCheckboxPress={toggleValue}
                         onSelectRow={openListValuePage}
                         onSelectAll={toggleAllValues}
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index acd5cde518e8..fdb51c11475c 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -1,6 +1,6 @@
 import {useFocusEffect, useIsFocused} from '@react-navigation/native';
 import {Str} from 'expensify-common';
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -30,6 +30,7 @@ import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
 import useNetwork from '@hooks/useNetwork';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {isConnectionInProgress, isConnectionUnverified} from '@libs/actions/connections';
@@ -92,8 +93,6 @@ function WorkspaceReportFieldsPage({
     const isConnectionVerified = connectedIntegration && !isConnectionUnverified(policy, connectedIntegration);
     const currentConnectionName = getCurrentConnectionName(policy);
 
-    const [inputValue, setInputValue] = useState('');
-
     const canSelectMultiple = !hasReportAccountingConnections && (isSmallScreenWidth ? selectionMode?.isEnabled : true);
 
     const fetchReportFields = useCallback(() => {
@@ -130,18 +129,11 @@ function WorkspaceReportFieldsPage({
         }));
     }, [canSelectMultiple, filteredPolicyFieldList, policy, selectedReportFields, translate]);
 
-    const [, startTransition] = useTransition();
-    const [filteredReportFieldsSections, setFilteredReportFieldsSections] = useState<ReportFieldForList[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? data.filter((reportField) => reportField.text.toLowerCase().includes(normalizedSearchQuery)) : data;
-            setFilteredReportFieldsSections(filtered.sort((a, b) => localeCompare(a.value, b.value)));
-        });
-    }, [inputValue, data]);
+    const filterReportField = useCallback((reportField: ReportFieldForList, searchInput: string) => !!reportField.text?.toLowerCase().includes(searchInput), []);
+    const sortReportFields = useCallback((reportFields: ReportFieldForList[]) => reportFields.sort((a, b) => localeCompare(a.value, b.value)), []);
+    const [inputValue, setInputValue, filteredReportFields] = useSearchResults(data, filterReportField, sortReportFields);
 
-    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredReportFieldsSections);
+    useAutoTurnSelectionModeOffWhenHasNoActiveOption(filteredReportFields);
 
     const updateSelectedReportFields = (item: ReportFieldForList) => {
         const fieldKey = getReportFieldKey(item.fieldID);
@@ -153,7 +145,7 @@ function WorkspaceReportFieldsPage({
 
     const toggleAllReportFields = () => {
         const availableReportFields = Object.values(filteredPolicyFieldList).filter(
-            (reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFieldsSections.find((item) => item.fieldID === reportField.fieldID),
+            (reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFields.find((item) => item.fieldID === reportField.fieldID),
         );
         setSelectedReportFields(selectedReportFields.length > 0 ? [] : availableReportFields);
     };
@@ -283,12 +275,12 @@ function WorkspaceReportFieldsPage({
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleReportField || isLoading) && getHeaderText()}
                 {!shouldShowEmptyState && !isLoading && shouldUseNarrowLayout && getHeaderText()}
-                {filteredReportFieldsSections.length > 15 && (
+                {filteredReportFields.length > 15 && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
                         onChangeText={setInputValue}
-                        shouldShowEmptyState={!shouldShowEmptyState && filteredReportFieldsSections.length === 0}
+                        shouldShowEmptyState={!shouldShowEmptyState && filteredReportFields.length === 0}
                     />
                 )}
                 {isLoading && (
@@ -298,7 +290,7 @@ function WorkspaceReportFieldsPage({
                         color={theme.spinner}
                     />
                 )}
-                {shouldShowEmptyState && filteredReportFieldsSections.length === 0 && (
+                {shouldShowEmptyState && filteredReportFields.length === 0 && (
                     <ScrollView contentContainerStyle={[styles.flexGrow1, styles.flexShrink0]}>
                         <EmptyStateComponent
                             title={translate('workspace.reportFields.emptyReportFields.title')}
@@ -319,7 +311,7 @@ function WorkspaceReportFieldsPage({
                         onTurnOnSelectionMode={(item) => item && updateSelectedReportFields(item)}
                         sections={[
                             {
-                                data: filteredReportFieldsSections,
+                                data: filteredReportFields,
                                 isDisabled: !policy,
                             },
                         ]}
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index 53feeb6173c3..4e32a1165884 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -1,5 +1,5 @@
 import lodashSortBy from 'lodash/sortBy';
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -31,6 +31,7 @@ import useNetwork from '@hooks/useNetwork';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useThreeDotsAnchorPosition from '@hooks/useThreeDotsAnchorPosition';
@@ -98,8 +99,6 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
 
     const {isOffline} = useNetwork({onReconnect: fetchTags});
 
-    const [inputValue, setInputValue] = useState('');
-
     useEffect(() => {
         fetchTags();
         // eslint-disable-next-line react-compiler/react-compiler
@@ -210,18 +209,9 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
         }));
     }, [isMultiLevelTags, policyTagLists, selectedTags, canSelectMultiple, translate, updateWorkspaceRequiresTag, updateWorkspaceTagEnabled]);
 
-    const [, startTransition] = useTransition();
-    const [filteredTagList, setFilteredTagList] = useState<TagListItem[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery
-                ? tagList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.value?.toLowerCase().includes(normalizedSearchQuery))
-                : tagList;
-            setFilteredTagList(lodashSortBy(filtered, 'value', localeCompare) as TagListItem[]);
-        });
-    }, [tagList, inputValue]);
+    const filterTag = useCallback((tag: TagListItem, searchInput: string) => !!tag.text?.toLowerCase().includes(searchInput) || !!tag.value?.toLowerCase().includes(searchInput), []);
+    const sortTags = useCallback((tags: TagListItem[]) => lodashSortBy(tags, 'value', localeCompare) as TagListItem[], []);
+    const [inputValue, setInputValue, filteredTagList] = useSearchResults(tagList, filterTag, sortTags);
 
     const filteredTagListKeyedByName = useMemo(
         () =>
diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
index 59c7a02739ba..97f41a75c046 100644
--- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
@@ -1,5 +1,5 @@
 import {useIsFocused} from '@react-navigation/native';
-import React, {useCallback, useEffect, useMemo, useRef, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
@@ -21,6 +21,7 @@ import useNetwork from '@hooks/useNetwork';
 import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
@@ -70,7 +71,6 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
     const currentTagListName = useMemo(() => getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]);
     const currentPolicyTag = policyTags?.[currentTagListName];
     const isQuickSettingsFlow = !!backTo;
-    const [inputValue, setInputValue] = useState('');
 
     const fetchTags = useCallback(() => {
         openPolicyTagsPage(policyID);
@@ -130,18 +130,9 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
         [currentPolicyTag?.tags, selectedTags, canSelectMultiple, translate, updateWorkspaceTagEnabled],
     );
 
-    const [, startTransition] = useTransition();
-    const [filteredTagList, setFilteredTagList] = useState<TagListItem[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery
-                ? tagList.filter((item) => !!item.text?.toLowerCase().includes(normalizedSearchQuery) || !!item.value?.toLowerCase().includes(normalizedSearchQuery))
-                : tagList;
-            setFilteredTagList(filtered.sort((tagA, tagB) => localeCompare(tagA.value, tagB.value)));
-        });
-    }, [tagList, inputValue]);
+    const filterTag = useCallback((tag: TagListItem, searchInput: string) => !!tag.text?.toLowerCase().includes(searchInput) || !!tag.value?.toLowerCase().includes(searchInput), []);
+    const sortTags = useCallback((tags: TagListItem[]) => tags.sort((tagA, tagB) => localeCompare(tagA.value, tagB.value)), []);
+    const [inputValue, setInputValue, filteredTagList] = useSearchResults(tagList, filterTag, sortTags);
 
     const hasDependentTags = useMemo(() => hasDependentTagsPolicyUtils(policy, policyTags), [policy, policyTags]);
 
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index 2c5f79efcfe9..59de35ae5d5e 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useEffect, useMemo, useState, useTransition} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
 import {ActivityIndicator, View} from 'react-native';
 import {useOnyx} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -24,6 +24,7 @@ import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
 import useNetwork from '@hooks/useNetwork';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchBackPress from '@hooks/useSearchBackPress';
+import useSearchResults from '@hooks/useSearchResults';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {isConnectionInProgress, isConnectionUnverified} from '@libs/actions/connections';
@@ -83,7 +84,6 @@ function WorkspaceTaxesPage({
 
     const enabledRatesCount = selectedTaxesIDs.filter((taxID) => !policy?.taxRates?.taxes[taxID]?.isDisabled).length;
     const disabledRatesCount = selectedTaxesIDs.length - enabledRatesCount;
-    const [inputValue, setInputValue] = useState('');
 
     const fetchTaxes = useCallback(() => {
         openPolicyTaxesPage(policyID);
@@ -183,22 +183,19 @@ function WorkspaceTaxesPage({
         });
     }, [policy, textForDefault, selectedTaxesIDs, canSelectMultiple, translate, updateWorkspaceTaxEnabled]);
 
-    const [, startTransition] = useTransition();
-    const [filteredTaxesList, setFilteredTaxesList] = useState<ListItem[]>([]);
-
-    useEffect(() => {
-        startTransition(() => {
-            const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery
-                ? taxesList.filter((tax) => {
-                      const taxName = tax.text?.toLowerCase() ?? '';
-                      const taxAlternateText = tax.alternateText?.toLowerCase() ?? '';
-                      return taxName.includes(normalizedSearchQuery) || taxAlternateText.includes(normalizedSearchQuery);
-                  })
-                : taxesList;
-            setFilteredTaxesList(filtered.sort((a, b) => (a.text ?? a.keyForList ?? '').localeCompare(b.text ?? b.keyForList ?? '')));
+    const filterTax = useCallback((tax: ListItem, searchInput: string) => {
+        const taxName = tax.text?.toLowerCase() ?? '';
+        const taxAlternateText = tax.alternateText?.toLowerCase() ?? '';
+        return taxName.includes(searchInput) || taxAlternateText.includes(searchInput);
+    }, []);
+    const sortTaxes = useCallback((taxes: ListItem[]) => {
+        return taxes.sort((a, b) => {
+            const aText = a.text ?? a.keyForList ?? '';
+            const bText = b.text ?? b.keyForList ?? '';
+            return aText.localeCompare(bText);
         });
-    }, [inputValue, taxesList]);
+    }, []);
+    const [inputValue, setInputValue, filteredTaxesList] = useSearchResults(taxesList, filterTax, sortTaxes);
 
     const isLoading = !isOffline && taxesList === undefined;
 

From a39f5c9f58a4cb3c66963753565ff60352ea1450 Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Mon, 5 May 2025 13:45:08 +0700
Subject: [PATCH 14/15] refactor: create const, fix delete error

---
 src/CONST.ts                                        |  1 +
 src/hooks/useSearchResults.ts                       | 13 ++++++++++++-
 src/pages/workspace/WorkspaceMembersPage.tsx        |  2 +-
 .../categories/WorkspaceCategoriesPage.tsx          |  2 +-
 .../distanceRates/PolicyDistanceRatesPage.tsx       |  4 ++--
 .../WorkspaceExpensifyCardListPage.tsx              |  2 +-
 .../workspace/perDiem/WorkspacePerDiemPage.tsx      |  2 +-
 .../reportFields/ReportFieldsListValuesPage.tsx     |  2 +-
 .../reportFields/WorkspaceReportFieldsPage.tsx      |  4 ++--
 src/pages/workspace/tags/WorkspaceTagsPage.tsx      |  2 +-
 src/pages/workspace/tags/WorkspaceViewTagsPage.tsx  |  2 +-
 src/pages/workspace/taxes/WorkspaceTaxesPage.tsx    |  2 +-
 12 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/src/CONST.ts b/src/CONST.ts
index 861c767fa48e..246928503d5f 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1995,6 +1995,7 @@ const CONST = {
         HEIGHT: 416,
     },
     DESKTOP_HEADER_PADDING: 12,
+    SEARCH_ITEM_LIMIT: 15,
     CATEGORY_SHORTCUT_BAR_HEIGHT: 32,
     SMALL_EMOJI_PICKER_SIZE: {
         WIDTH: '100%',
diff --git a/src/hooks/useSearchResults.ts b/src/hooks/useSearchResults.ts
index 5bd5d85cd100..8798f2562abb 100644
--- a/src/hooks/useSearchResults.ts
+++ b/src/hooks/useSearchResults.ts
@@ -1,4 +1,6 @@
 import {useEffect, useState, useTransition} from 'react';
+import CONST from '@src/CONST';
+import usePrevious from './usePrevious';
 
 /**
  * This hook filters (and optionally sorts) a dataset based on a search parameter.
@@ -9,14 +11,23 @@ function useSearchResults<TValue>(data: TValue[], filterData: (datum: TValue, se
     const [inputValue, setInputValue] = useState('');
     const [result, setResult] = useState(data);
     const [, startTransition] = useTransition();
+    const prevData = usePrevious(data);
     useEffect(() => {
         startTransition(() => {
             const normalizedSearchQuery = inputValue.trim().toLowerCase();
-            const filtered = normalizedSearchQuery ? data.filter((item) => filterData(item, inputValue)) : data;
+            const filtered = normalizedSearchQuery.length ? data.filter((item) => filterData(item, normalizedSearchQuery)) : data;
             const sorted = sortData(filtered);
             setResult(sorted);
         });
     }, [data, filterData, inputValue, sortData]);
+
+    useEffect(() => {
+        if (prevData.length <= CONST.SEARCH_ITEM_LIMIT || data.length > CONST.SEARCH_ITEM_LIMIT) {
+            return;
+        }
+        setInputValue('');
+    }, [data, prevData]);
+
     return [inputValue, setInputValue, result] as const;
 }
 
diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index c5f7e5b04353..4bd342da6a07 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -723,7 +723,7 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson
                 <>
                     {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
                     {shouldUseNarrowLayout ? <View style={[styles.pr5]}>{getHeaderContent()}</View> : getHeaderContent()}
-                    {data.length > 15 && (
+                    {data.length > CONST.SEARCH_ITEM_LIMIT && (
                         <SearchBar
                             inputValue={inputValue}
                             onChangeText={setInputValue}
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 015e68243856..898c37c7e291 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -439,7 +439,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
                 />
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
                 {hasVisibleCategories && !isLoading && getHeaderText()}
-                {categoryList.length > 15 && (
+                {categoryList.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.categories.findCategory')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index 2dbd8765f801..69fdf3df33f3 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -251,7 +251,7 @@ function PolicyDistanceRatesPage({
         } else {
             setSelectedDistanceRates(
                 Object.entries(selectableRates)
-                    .filter(([, rate]) => rate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
+                    .filter(([, rate]) => rate.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredDistanceRatesList.some((item) => item.value === rate.customUnitRateID))
                     .map(([key]) => key),
             );
         }
@@ -376,7 +376,7 @@ function PolicyDistanceRatesPage({
                 </HeaderWithBackButton>
                 {shouldUseNarrowLayout && <View style={[styles.ph5]}>{headerButtons}</View>}
                 {Object.values(customUnitRates).length > 0 && getHeaderText()}
-                {Object.values(customUnitRates).length > 15 && (
+                {Object.values(customUnitRates).length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.distanceRates.findRate')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index 19eb1969fd79..a8d1d56d2f35 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -184,7 +184,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp
                             policyID={policyID}
                             cardSettings={cardSettings}
                         />
-                        {allCards.length > 15 && (
+                        {allCards.length > CONST.SEARCH_ITEM_LIMIT && (
                             <SearchBar
                                 label={translate('workspace.expensifyCard.findCard')}
                                 inputValue={inputValue}
diff --git a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
index 9834b33d3a38..a3d4271756c8 100644
--- a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
+++ b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
@@ -416,7 +416,7 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) {
                 />
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
                 {(!hasVisibleSubRates || isLoading) && getHeaderText()}
-                {subRatesList.length > 15 && (
+                {subRatesList.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.perDiem.findPerDiemRate')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
index 1ca548aaf315..4d01113571ea 100644
--- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
+++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx
@@ -330,7 +330,7 @@ function ReportFieldsListValuesPage({
                 <View style={[styles.ph5, styles.pv4]}>
                     <Text style={[styles.sidebarLinkText, styles.optionAlternateText]}>{translate('workspace.reportFields.listInputSubtitle')}</Text>
                 </View>
-                {filteredListValues.length > 15 && (
+                {filteredListValues.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
index c7a8a7a89aa5..0326b776d7f0 100644
--- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
+++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx
@@ -155,7 +155,7 @@ function WorkspaceReportFieldsPage({
         const availableReportFields = Object.values(reportFieldsSections).filter(
             (reportField) => reportField.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && filteredReportFields.find((item) => item.fieldID === reportField.fieldID),
         );
-        setSelectedReportFields(selectedReportFields.length > 0 ? [] : Object.keys(availableReportFields));
+        setSelectedReportFields(selectedReportFields.length > 0 ? [] : Object.values(availableReportFields).map((reportField) => getReportFieldKey(reportField.fieldID)));
     };
 
     const navigateToReportFieldsSettings = (reportField: ReportFieldForList) => {
@@ -282,7 +282,7 @@ function WorkspaceReportFieldsPage({
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleReportField || isLoading) && getHeaderText()}
                 {!shouldShowEmptyState && !isLoading && shouldUseNarrowLayout && getHeaderText()}
-                {filteredReportFields.length > 15 && (
+                {reportFieldsSections.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.reportFields.findReportField')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index 4e32a1165884..fab5709f22ec 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -485,7 +485,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
                     danger
                 />
                 {(!shouldUseNarrowLayout || !hasVisibleTags || isLoading) && getHeaderText()}
-                {tagList.length > 15 && (
+                {tagList.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.tags.findTag')}
                         inputValue={inputValue}
diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
index e1dfb90f552e..5eab6fd7b022 100644
--- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
@@ -348,7 +348,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
                         color={theme.spinner}
                     />
                 )}
-                {tagList.length > 15 && (
+                {tagList.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         inputValue={inputValue}
                         onChangeText={setInputValue}
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index 59de35ae5d5e..d28469d97744 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -385,7 +385,7 @@ function WorkspaceTaxesPage({
                 {shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{headerButtons}</View>}
 
                 {getHeaderText()}
-                {taxesList.length > 15 && (
+                {taxesList.length > CONST.SEARCH_ITEM_LIMIT && (
                     <SearchBar
                         label={translate('workspace.taxes.findTaxRate')}
                         inputValue={inputValue}

From 1dc7cc2d7656a2e510efc6d284a5a9aebe81e09c Mon Sep 17 00:00:00 2001
From: daledah <daleda261@outlook.com>
Date: Wed, 7 May 2025 14:46:53 +0700
Subject: [PATCH 15/15] fix: test

---
 tests/unit/CardUtilsTest.ts | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 0bffc32ab0f9..947b67a3570b 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -10,6 +10,7 @@ import {
     getBankCardDetailsImage,
     getBankName,
     getCardFeedIcon,
+    getCardsByCardholderName,
     getCompanyFeeds,
     getCustomOrFormattedFeedName,
     getFeedType,
@@ -923,7 +924,8 @@ describe('CardUtils', () => {
 
         it('should sort cards by cardholder name in ascending order', () => {
             const policyMembersAccountIDs = [1, 2, 3];
-            const sortedCards = sortCardsByCardholderName(mockCards, mockPersonalDetails, policyMembersAccountIDs);
+            const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
+            const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails);
 
             expect(sortedCards).toHaveLength(3);
             expect(sortedCards.at(0)?.cardID).toBe(2);
@@ -933,7 +935,8 @@ describe('CardUtils', () => {
 
         it('should filter out cards that are not associated with policy members', () => {
             const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3
-            const sortedCards = sortCardsByCardholderName(mockCards, mockPersonalDetails, policyMembersAccountIDs);
+            const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
+            const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails);
 
             expect(sortedCards).toHaveLength(2);
             expect(sortedCards.at(0)?.cardID).toBe(2);
@@ -942,14 +945,16 @@ describe('CardUtils', () => {
 
         it('should handle undefined cardsList', () => {
             const policyMembersAccountIDs = [1, 2, 3];
-            const sortedCards = sortCardsByCardholderName(undefined, mockPersonalDetails, policyMembersAccountIDs);
+            const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs);
+            const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails);
 
             expect(sortedCards).toHaveLength(0);
         });
 
         it('should handle undefined personalDetails', () => {
             const policyMembersAccountIDs = [1, 2, 3];
-            const sortedCards = sortCardsByCardholderName(mockCards, undefined, policyMembersAccountIDs);
+            const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs);
+            const sortedCards = sortCardsByCardholderName(cards, undefined);
 
             expect(sortedCards).toHaveLength(3);
             // All cards should be sorted with default names
@@ -986,7 +991,8 @@ describe('CardUtils', () => {
             };
 
             const policyMembersAccountIDs = [1, 2];
-            const sortedCards = sortCardsByCardholderName(cardsWithMissingAccountID, mockPersonalDetails, policyMembersAccountIDs);
+            const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs);
+            const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails);
 
             expect(sortedCards).toHaveLength(1);
             expect(sortedCards.at(0)?.cardID).toBe(1);
