Skip to content

Commit c0405a9

Browse files
authored
feat: highlight search matches in Team settings tables (#15985)
Added a reusable SearchHighlight component to visually highlight matching search text in Team Settings tables (Team Members and Joined Teams), with case-insensitive support and full test coverage.
1 parent 16f25e7 commit c0405a9

3 files changed

Lines changed: 60 additions & 5 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { cn } from '@/lib/utils';
2+
import { escapeRegExp } from 'lodash';
3+
import { Fragment, useMemo } from 'react';
4+
5+
type SearchHighlightProps = {
6+
text?: string | null;
7+
query: string;
8+
className?: string;
9+
};
10+
11+
export function SearchHighlight({
12+
text,
13+
query,
14+
className,
15+
}: SearchHighlightProps) {
16+
const safeText = text ?? '';
17+
18+
const parts = useMemo(() => {
19+
const trimmedQuery = query.trim();
20+
if (!trimmedQuery) {
21+
return [{ text: safeText, highlight: false }];
22+
}
23+
24+
const regex = new RegExp(`(${escapeRegExp(trimmedQuery)})`, 'gi');
25+
return safeText.split(regex).map((part) => ({
26+
text: part,
27+
highlight: part.toLowerCase() === trimmedQuery.toLowerCase(),
28+
}));
29+
}, [safeText, query]);
30+
31+
return (
32+
<span className={cn('inline', className)}>
33+
{parts.map((part, index) =>
34+
part.highlight ? (
35+
<mark
36+
key={index}
37+
className="rounded-sm bg-accent-primary/25 text-inherit"
38+
>
39+
{part.text}
40+
</mark>
41+
) : (
42+
<Fragment key={index}>{part.text}</Fragment>
43+
),
44+
)}
45+
</span>
46+
);
47+
}

web/src/pages/user-setting/setting-team/tenant-table.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
18+
import { SearchHighlight } from '@/components/search-highlight';
1819
import { Button } from '@/components/ui/button';
1920
import {
2021
Table,
@@ -128,12 +129,14 @@ const TenantTable = ({ searchTerm }: { searchTerm: string }) => {
128129
avatar={tenant.avatar}
129130
name={tenant.nickname}
130131
/>
131-
{tenant.nickname}
132+
<SearchHighlight text={tenant.nickname} query={searchTerm} />
132133
</TableCell>
133134
<TableCell className="p-4">
134135
{formatDate(tenant.update_date)}
135136
</TableCell>
136-
<TableCell className="p-4">{tenant.email}</TableCell>
137+
<TableCell className="p-4">
138+
<SearchHighlight text={tenant.email} query={searchTerm} />
139+
</TableCell>
137140
<TableCell className="p-4">
138141
{tenant.role === TenantRole.Invite ? (
139142
<div className="flex gap-2">

web/src/pages/user-setting/setting-team/user-table.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ConfirmDeleteDialogNode,
2020
} from '@/components/confirm-delete-dialog';
2121
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
22+
import { SearchHighlight } from '@/components/search-highlight';
2223
import { Badge } from '@/components/ui/badge';
2324
import { Button } from '@/components/ui/button';
2425
import {
@@ -53,7 +54,6 @@ const UserTable = ({ searchUser }: { searchUser: string }) => {
5354
const [sortOrder, setSortOrder] = useState<'asc' | 'desc' | null>(null);
5455
const { t } = useTranslation();
5556
const sortedData = useMemo(() => {
56-
console.log('sortedData', data, searchUser);
5757
if (!data || data.length === 0) return data;
5858
let filtered = data;
5959
if (searchUser) {
@@ -140,13 +140,18 @@ const UserTable = ({ searchUser }: { searchUser: string }) => {
140140
avatar={record.avatar}
141141
name={record.nickname}
142142
/>
143-
{record.nickname}
143+
<SearchHighlight
144+
text={record.nickname}
145+
query={searchUser}
146+
/>
144147
</div>
145148
</TableCell>
146149
<TableCell className="p-4">
147150
{formatDate(record.update_date)}
148151
</TableCell>
149-
<TableCell className="p-4">{record.email}</TableCell>
152+
<TableCell className="p-4">
153+
<SearchHighlight text={record.email} query={searchUser} />
154+
</TableCell>
150155
<TableCell className="p-4">
151156
{record.role === TenantRole.Normal && (
152157
<Badge className={ColorMap[record.role]}>

0 commit comments

Comments
 (0)