feat: implement client-side state management hooks
- Created useSearch hook for search state and debouncing - Created useFilters hook for filter state management - Created useOptimisticUpdate hook for optimistic UI updates - Created useScrollPosition hook for scroll preservation - Updated SearchBar, GlobalSearch, and ListDetailView to use new hooks - Added comprehensive documentation for all hooks Validates: Requirements 6.4, 7.3, 7.4, 8.3 Task: 9. Implement client-side state management
This commit is contained in:
97
hooks/useFilters.ts
Normal file
97
hooks/useFilters.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback, useMemo } from 'react'
|
||||
|
||||
export interface FilterState {
|
||||
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'
|
||||
}
|
||||
|
||||
interface UseFiltersOptions {
|
||||
initialFilters?: Partial<FilterState>
|
||||
onFiltersChange?: (filters: FilterState) => void | Promise<void>
|
||||
}
|
||||
|
||||
interface UseFiltersReturn {
|
||||
filters: FilterState
|
||||
setFilters: (filters: FilterState) => void
|
||||
updateFilter: <K extends keyof FilterState>(key: K, value: FilterState[K]) => void
|
||||
clearFilters: () => void
|
||||
hasActiveFilters: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_FILTERS: FilterState = {
|
||||
priority: null,
|
||||
completed: null,
|
||||
tags: [],
|
||||
dueDateRange: { start: null, end: null },
|
||||
sortBy: 'createdAt',
|
||||
sortOrder: 'desc'
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for managing filter state
|
||||
* Validates: Requirements 6.4
|
||||
*/
|
||||
export function useFilters(options: UseFiltersOptions = {}): UseFiltersReturn {
|
||||
const { initialFilters = {}, onFiltersChange } = options
|
||||
|
||||
const [filters, setFiltersState] = useState<FilterState>({
|
||||
...DEFAULT_FILTERS,
|
||||
...initialFilters
|
||||
})
|
||||
|
||||
// Check if any filters are active (excluding sort options)
|
||||
const hasActiveFilters = useMemo(() => {
|
||||
return (
|
||||
filters.priority !== null ||
|
||||
filters.completed !== null ||
|
||||
filters.tags.length > 0 ||
|
||||
filters.dueDateRange.start !== null ||
|
||||
filters.dueDateRange.end !== null
|
||||
)
|
||||
}, [filters])
|
||||
|
||||
// Set filters with callback
|
||||
const setFilters = useCallback((newFilters: FilterState) => {
|
||||
setFiltersState(newFilters)
|
||||
if (onFiltersChange) {
|
||||
onFiltersChange(newFilters)
|
||||
}
|
||||
}, [onFiltersChange])
|
||||
|
||||
// Update a single filter property
|
||||
const updateFilter = useCallback(<K extends keyof FilterState>(
|
||||
key: K,
|
||||
value: FilterState[K]
|
||||
) => {
|
||||
setFiltersState(prev => {
|
||||
const newFilters = { ...prev, [key]: value }
|
||||
if (onFiltersChange) {
|
||||
onFiltersChange(newFilters)
|
||||
}
|
||||
return newFilters
|
||||
})
|
||||
}, [onFiltersChange])
|
||||
|
||||
// Clear all filters (reset to defaults)
|
||||
const clearFilters = useCallback(() => {
|
||||
const clearedFilters = { ...DEFAULT_FILTERS }
|
||||
setFiltersState(clearedFilters)
|
||||
if (onFiltersChange) {
|
||||
onFiltersChange(clearedFilters)
|
||||
}
|
||||
}, [onFiltersChange])
|
||||
|
||||
return {
|
||||
filters,
|
||||
setFilters,
|
||||
updateFilter,
|
||||
clearFilters,
|
||||
hasActiveFilters
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user