diff --git a/lib/actions.ts b/lib/actions.ts index c56c24b..e8f958e 100644 --- a/lib/actions.ts +++ b/lib/actions.ts @@ -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 } \ No newline at end of file diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..3b2eee6 --- /dev/null +++ b/lib/utils.ts @@ -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 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, '$1') +} + +/** + * 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, '\\$&') +}