Files
Todo/hooks/useFilters.ts
Paul Bailey e2e3581d06 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
2025-12-01 20:11:41 +00:00

98 lines
2.5 KiB
TypeScript

'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
}
}