Added a util helper to remove accent and case to improve search logic (#14533)

# [WIP] Make Local Search Non-Accent-Sensitive

fixes(#14468)

---

**Description:**  
This PR adds accent- and case-insensitive search for the
**SettingsWorkspaceMembers** component’s local search. It ensures that
searching for names or emails works correctly even if the user types
letters without accents.

**Implementation:**  
- Added a helper util: `removeAccentsAndCase(text: string)`  
- This util:
  - Normalizes text to NFD form
  - Removes diacritics (accents)
  - Converts text to lowercase
- Replaced current `.includes(searchFilter)` logic with a normalized
comparison:


---

### Video ->

[Screencast from 2025-09-16
16-42-50.webm](https://github.com/user-attachments/assets/7035906c-9c5d-414d-81e5-74e53803628a)


---

Opening a draft pr for initial strategy review before extending it to
other components.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Saurav Jain
2025-09-19 20:33:43 +05:30
committed by GitHub
parent deed08ba25
commit 8ba1dc1d94
17 changed files with 315 additions and 151 deletions
@@ -20,6 +20,7 @@ import { useRecoilState } from 'recoil';
import { IconSearch } from 'twenty-ui/display';
import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem';
import { type SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem';
import { normalizeSearchText } from '~/utils/normalizeSearchText';
const GET_SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD: TableMetadata<SettingsObjectDetailTableItem> =
{
@@ -155,25 +156,25 @@ export const SettingsObjectFieldTable = ({
tableMetadata,
);
const filteredActiveItems = useMemo(
() =>
sortedActiveObjectSettingsDetailItems.filter(
(item) =>
item.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.dataType.toLowerCase().includes(searchTerm.toLowerCase()),
),
[sortedActiveObjectSettingsDetailItems, searchTerm],
);
const filteredActiveItems = useMemo(() => {
const searchNormalized = normalizeSearchText(searchTerm);
return sortedActiveObjectSettingsDetailItems.filter((item) => {
return (
normalizeSearchText(item.label).includes(searchNormalized) ||
normalizeSearchText(item.dataType).includes(searchNormalized)
);
});
}, [sortedActiveObjectSettingsDetailItems, searchTerm]);
const filteredDisabledItems = useMemo(
() =>
sortedDisabledObjectSettingsDetailItems.filter(
(item) =>
item.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.dataType.toLowerCase().includes(searchTerm.toLowerCase()),
),
[sortedDisabledObjectSettingsDetailItems, searchTerm],
);
const filteredDisabledItems = useMemo(() => {
const searchNormalized = normalizeSearchText(searchTerm);
return sortedDisabledObjectSettingsDetailItems.filter((item) => {
return (
normalizeSearchText(item.label).includes(searchNormalized) ||
normalizeSearchText(item.dataType).includes(searchNormalized)
);
});
}, [sortedDisabledObjectSettingsDetailItems, searchTerm]);
return (
<>