useFilteredBlocks.ts
1 import { useMemo, useState } from 'react'; 2 import { useBlocks } from './useBlocks'; 3 import { getBlockConfidencePercentage } from '@/utils/blockUtils'; 4 5 // Define sort options 6 export type SortOption = 7 | 'confidenceDesc' 8 | 'confidenceAsc' 9 | 'createdAtDesc' 10 | 'createdAtAsc' 11 | 'typeAsc' 12 | 'typeDesc'; 13 14 interface UseFilteredBlocksOptions { 15 initialSearchQuery?: string; 16 initialSortBy?: SortOption; 17 initialBlockType?: string | null; 18 } 19 20 /** 21 * Hook for fetching, filtering, and sorting memory blocks 22 */ 23 export function useFilteredBlocks(options: UseFilteredBlocksOptions = {}) { 24 // Get blocks from the API 25 const { blocks, isLoading, isError, mutate } = useBlocks(); 26 27 // State for filters 28 const [searchQuery, setSearchQuery] = useState(options.initialSearchQuery || ''); 29 const [sortBy, setSortBy] = useState<SortOption>(options.initialSortBy || 'confidenceDesc'); 30 const [blockType, setBlockType] = useState<string | null>(options.initialBlockType || null); 31 32 // Apply filters and sort 33 const filteredAndSortedBlocks = useMemo(() => { 34 if (!blocks) return []; 35 36 // First, filter blocks by search query 37 const filtered = blocks.filter(block => { 38 const lowerQuery = searchQuery.toLowerCase(); 39 40 // Skip filtering if search is empty 41 if (!searchQuery) return true; 42 43 // Filter by text content 44 if (block.text?.toLowerCase().includes(lowerQuery)) return true; 45 46 // Filter by metadata (depends on block type) 47 if (block.metadata) { 48 // For project blocks, also check name and description 49 if (block.type === 'project' && typeof block.metadata.name === 'string') { 50 if (block.metadata.name.toLowerCase().includes(lowerQuery)) return true; 51 } 52 53 // For doc blocks, also check title 54 if (block.type === 'doc' && typeof block.metadata.title === 'string') { 55 if (block.metadata.title.toLowerCase().includes(lowerQuery)) return true; 56 } 57 } 58 59 return false; 60 }); 61 62 // Then, filter by block type if specified 63 const typeFiltered = blockType 64 ? filtered.filter(block => block.type === blockType) 65 : filtered; 66 67 // Finally, sort the blocks 68 return [...typeFiltered].sort((a, b) => { 69 switch (sortBy) { 70 case 'confidenceDesc': 71 return getBlockConfidencePercentage(b) - getBlockConfidencePercentage(a); 72 case 'confidenceAsc': 73 return getBlockConfidencePercentage(a) - getBlockConfidencePercentage(b); 74 case 'createdAtDesc': 75 return new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime(); 76 case 'createdAtAsc': 77 return new Date(a.created_at || 0).getTime() - new Date(b.created_at || 0).getTime(); 78 case 'typeAsc': 79 return a.type.localeCompare(b.type); 80 case 'typeDesc': 81 return b.type.localeCompare(a.type); 82 default: 83 return 0; 84 } 85 }); 86 }, [blocks, searchQuery, sortBy, blockType]); 87 88 return { 89 blocks: filteredAndSortedBlocks, 90 totalBlocks: blocks?.length || 0, 91 filteredCount: filteredAndSortedBlocks.length, 92 isLoading, 93 isError, 94 searchQuery, 95 setSearchQuery, 96 sortBy, 97 setSortBy, 98 blockType, 99 setBlockType, 100 refresh: mutate 101 }; 102 }