Implement filtering and sorting functionality
- Add getItemsByFilter server action with support for: - Priority filtering (low, medium, high) - Completed status filtering - Tag filtering with compound AND logic - Due date range filtering - Sorting by createdAt, dueDate, priority, and title - Ascending and descending sort order - Handle null due dates in sorting (placed at end) - Implement compound filter logic with AND conditions - Requirements: 6.1, 6.2, 6.3, 6.5
This commit is contained in:
106
lib/actions.ts
106
lib/actions.ts
@@ -178,8 +178,8 @@ export async function searchItems(query: string, listId?: string) {
|
|||||||
|
|
||||||
const tagMatches = allItems.filter((item) => {
|
const tagMatches = allItems.filter((item) => {
|
||||||
const tags = Array.isArray(item.tags) ? item.tags : []
|
const tags = Array.isArray(item.tags) ? item.tags : []
|
||||||
return tags.some((tag: string) =>
|
return tags.some((tag) =>
|
||||||
tag.toLowerCase().includes(searchTerm)
|
typeof tag === 'string' && tag.toLowerCase().includes(searchTerm)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -190,4 +190,106 @@ export async function searchItems(query: string, listId?: string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return uniqueResults
|
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
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user