Implement search functionality - Add searchItems server action with list-scoped and global search - Add highlightText utility function for search result highlighting - Search across title, description, notes, and tags fields - Requirements: 8.1, 8.2, 8.5
This commit is contained in:
@@ -118,4 +118,76 @@ export async function updateList(
|
||||
revalidatePath('/')
|
||||
revalidatePath(`/list/${listId}`)
|
||||
return updated
|
||||
}
|
||||
|
||||
export async function searchItems(query: string, listId?: string) {
|
||||
if (!query.trim()) {
|
||||
return []
|
||||
}
|
||||
|
||||
const searchTerm = query.toLowerCase()
|
||||
|
||||
// Build the where clause
|
||||
const whereClause: any = {
|
||||
OR: [
|
||||
{
|
||||
title: {
|
||||
contains: query,
|
||||
mode: 'insensitive' as const,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: {
|
||||
contains: query,
|
||||
mode: 'insensitive' as const,
|
||||
},
|
||||
},
|
||||
{
|
||||
notes: {
|
||||
contains: query,
|
||||
mode: 'insensitive' as const,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// Add list scope if specified
|
||||
if (listId) {
|
||||
whereClause.listId = listId
|
||||
}
|
||||
|
||||
const items = await prisma.item.findMany({
|
||||
where: whereClause,
|
||||
include: {
|
||||
list: true,
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: 'desc',
|
||||
},
|
||||
})
|
||||
|
||||
// Also search in tags (stored as JSON)
|
||||
const allItems = listId
|
||||
? await prisma.item.findMany({
|
||||
where: { listId },
|
||||
include: { list: true },
|
||||
})
|
||||
: await prisma.item.findMany({
|
||||
include: { list: true },
|
||||
})
|
||||
|
||||
const tagMatches = allItems.filter((item) => {
|
||||
const tags = Array.isArray(item.tags) ? item.tags : []
|
||||
return tags.some((tag: string) =>
|
||||
tag.toLowerCase().includes(searchTerm)
|
||||
)
|
||||
})
|
||||
|
||||
// Combine results and remove duplicates
|
||||
const combinedResults = [...items, ...tagMatches]
|
||||
const uniqueResults = Array.from(
|
||||
new Map(combinedResults.map((item) => [item.id, item])).values()
|
||||
)
|
||||
|
||||
return uniqueResults
|
||||
}
|
||||
23
lib/utils.ts
Normal file
23
lib/utils.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Highlights matching text in a string by wrapping matches with a marker
|
||||
* @param text - The text to search in
|
||||
* @param query - The search query to highlight
|
||||
* @returns The text with highlighted matches wrapped in <mark> tags
|
||||
*/
|
||||
export function highlightText(text: string, query: string): string {
|
||||
if (!query.trim() || !text) {
|
||||
return text
|
||||
}
|
||||
|
||||
const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi')
|
||||
return text.replace(regex, '<mark>$1</mark>')
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes special regex characters in a string
|
||||
* @param string - The string to escape
|
||||
* @returns The escaped string safe for use in RegExp
|
||||
*/
|
||||
function escapeRegExp(string: string): string {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
Reference in New Issue
Block a user