'use server' import { prisma } from './db' import { revalidatePath } from 'next/cache' export async function getLists() { return await prisma.list.findMany({ include: { items: true, }, orderBy: { updatedAt: 'desc', }, }) } export async function createList(title: string, color: string = '#3B82F6') { const list = await prisma.list.create({ data: { title, color, }, }) revalidatePath('/') return list } export async function getListWithItems(listId: string) { return await prisma.list.findUnique({ where: { id: listId }, include: { items: { orderBy: { createdAt: 'desc', }, }, }, }) } export async function getItem(itemId: string) { return await prisma.item.findUnique({ where: { id: itemId }, include: { list: true, }, }) } export async function createItem( listId: string, title: string, description?: string, metadata?: { priority?: 'low' | 'medium' | 'high' dueDate?: Date | null tags?: string[] notes?: string | null } ) { const item = await prisma.item.create({ data: { title, description, listId, ...(metadata?.priority && { priority: metadata.priority }), ...(metadata?.dueDate !== undefined && { dueDate: metadata.dueDate }), ...(metadata?.tags && { tags: metadata.tags }), ...(metadata?.notes !== undefined && { notes: metadata.notes }), }, }) revalidatePath(`/list/${listId}`) return item } export async function toggleItem(itemId: string) { const item = await prisma.item.findUnique({ where: { id: itemId } }) if (!item) return null const updated = await prisma.item.update({ where: { id: itemId }, data: { completed: !item.completed }, }) revalidatePath(`/list/${item.listId}`) return updated } export async function updateItem( itemId: string, data: { title?: string description?: string | null completed?: boolean priority?: 'low' | 'medium' | 'high' dueDate?: Date | null tags?: string[] notes?: string | null } ) { const item = await prisma.item.findUnique({ where: { id: itemId } }) if (!item) return null const updated = await prisma.item.update({ where: { id: itemId }, data, }) revalidatePath(`/list/${item.listId}`) return updated } export async function deleteItem(itemId: string) { const item = await prisma.item.findUnique({ where: { id: itemId } }) if (!item) return null await prisma.item.delete({ where: { id: itemId }, }) revalidatePath(`/list/${item.listId}`) return { success: true } } export async function deleteList(listId: string) { await prisma.list.delete({ where: { id: listId }, }) revalidatePath('/') return { success: true } } export async function updateList( listId: string, data: { title?: string color?: string } ) { const updated = await prisma.list.update({ where: { id: listId }, data, }) 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) => typeof 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 } export async function getItemsByFilter( listId: string | null, filters: { priority?: 'low' | 'medium' | 'high' | null completed?: boolean | null tags?: string[] dueDateRange?: { start: Date | null; end: Date | null } sortBy?: 'createdAt' | 'dueDate' | 'priority' | 'title' sortOrder?: 'asc' | 'desc' } = {} ) { // Build the where clause with compound filters (AND logic) const whereClause: any = {} // Add list scope if specified if (listId) { whereClause.listId = listId } // Filter by priority if (filters.priority) { whereClause.priority = filters.priority } // Filter by completed status if (filters.completed !== undefined && filters.completed !== null) { whereClause.completed = filters.completed } // Filter by due date range if (filters.dueDateRange) { const { start, end } = filters.dueDateRange if (start || end) { whereClause.dueDate = {} if (start) { whereClause.dueDate.gte = start } if (end) { whereClause.dueDate.lte = end } } } // Fetch items from database const items = await prisma.item.findMany({ where: whereClause, include: { list: true, }, }) // Filter by tags (client-side since tags are stored as JSON) let filteredItems = items if (filters.tags && filters.tags.length > 0) { filteredItems = items.filter((item) => { const itemTags = Array.isArray(item.tags) ? item.tags : [] return filters.tags!.every((filterTag) => itemTags.some((itemTag) => typeof itemTag === 'string' && itemTag === filterTag) ) }) } // Sort items const sortBy = filters.sortBy || 'createdAt' const sortOrder = filters.sortOrder || 'desc' const sortedItems = [...filteredItems].sort((a, b) => { let comparison = 0 switch (sortBy) { case 'createdAt': comparison = a.createdAt.getTime() - b.createdAt.getTime() break case 'dueDate': // Handle null due dates - place at end if (a.dueDate === null && b.dueDate === null) { comparison = 0 } else if (a.dueDate === null) { comparison = 1 // a goes after b } else if (b.dueDate === null) { comparison = -1 // b goes after a } else { comparison = a.dueDate.getTime() - b.dueDate.getTime() } break case 'priority': const priorityOrder = { low: 1, medium: 2, high: 3 } const aPriority = priorityOrder[a.priority as keyof typeof priorityOrder] || 2 const bPriority = priorityOrder[b.priority as keyof typeof priorityOrder] || 2 comparison = aPriority - bPriority break case 'title': comparison = a.title.localeCompare(b.title) break } return sortOrder === 'asc' ? comparison : -comparison }) return sortedItems }