1+ import { useState , useMemo } from 'react' ;
2+ import { Subscription , SubscriptionCategory , BillingCycle } from '../types/subscription' ;
3+
4+ export const useSubscriptionFilters = ( subscriptions : Subscription [ ] ) => {
5+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
6+ const [ selectedCategories , setSelectedCategories ] = useState < SubscriptionCategory [ ] > ( [ ] ) ;
7+ const [ selectedBillingCycles , setSelectedBillingCycles ] = useState < BillingCycle [ ] > ( [ ] ) ;
8+ const [ priceRange , setPriceRange ] = useState ( { min : 0 , max : 1000 } ) ;
9+ const [ showActiveOnly , setShowActiveOnly ] = useState ( true ) ;
10+ const [ showCryptoOnly , setShowCryptoOnly ] = useState ( false ) ;
11+ const [ sortBy , setSortBy ] = useState < 'name' | 'price' | 'nextBilling' | 'category' > ( 'name' ) ;
12+ const [ sortOrder , setSortOrder ] = useState < 'asc' | 'desc' > ( 'asc' ) ;
13+
14+ const filteredAndSorted = useMemo ( ( ) => {
15+ let filtered = subscriptions || [ ] ;
16+
17+ if ( searchQuery . trim ( ) ) {
18+ filtered = filtered . filter ( sub =>
19+ sub . name . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ||
20+ sub . description ?. toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
21+ ) ;
22+ }
23+
24+ if ( selectedCategories . length > 0 ) {
25+ filtered = filtered . filter ( sub => selectedCategories . includes ( sub . category ) ) ;
26+ }
27+
28+ if ( selectedBillingCycles . length > 0 ) {
29+ filtered = filtered . filter ( sub => selectedBillingCycles . includes ( sub . billingCycle ) ) ;
30+ }
31+
32+ filtered = filtered . filter ( sub => sub . price >= priceRange . min && sub . price <= priceRange . max ) ;
33+ if ( showActiveOnly ) filtered = filtered . filter ( sub => sub . isActive ) ;
34+ if ( showCryptoOnly ) filtered = filtered . filter ( sub => sub . isCryptoEnabled ) ;
35+
36+ return [ ...filtered ] . sort ( ( a , b ) => {
37+ let comp = 0 ;
38+ switch ( sortBy ) {
39+ case 'name' : comp = a . name . localeCompare ( b . name ) ; break ;
40+ case 'price' : comp = a . price - b . price ; break ;
41+ case 'nextBilling' : comp = new Date ( a . nextBillingDate ) . getTime ( ) - new Date ( b . nextBillingDate ) . getTime ( ) ; break ;
42+ case 'category' : comp = a . category . localeCompare ( b . category ) ; break ;
43+ }
44+ return sortOrder === 'asc' ? comp : - comp ;
45+ } ) ;
46+ } , [ subscriptions , searchQuery , selectedCategories , selectedBillingCycles , priceRange , showActiveOnly , showCryptoOnly , sortBy , sortOrder ] ) ;
47+
48+ const activeFilterCount = useMemo ( ( ) => {
49+ let count = 0 ;
50+ if ( searchQuery . trim ( ) ) count ++ ;
51+ if ( selectedCategories . length > 0 ) count ++ ;
52+ if ( selectedBillingCycles . length > 0 ) count ++ ;
53+ if ( priceRange . min > 0 || priceRange . max < 1000 ) count ++ ;
54+ if ( ! showActiveOnly ) count ++ ;
55+ if ( showCryptoOnly ) count ++ ;
56+ if ( sortBy !== 'name' || sortOrder !== 'asc' ) count ++ ;
57+ return count ;
58+ } , [ searchQuery , selectedCategories , selectedBillingCycles , priceRange , showActiveOnly , showCryptoOnly , sortBy , sortOrder ] ) ;
59+
60+ return {
61+ filters : {
62+ searchQuery, setSearchQuery,
63+ selectedCategories, setSelectedCategories,
64+ selectedBillingCycles, setSelectedBillingCycles,
65+ priceRange, setPriceRange,
66+ showActiveOnly, setShowActiveOnly,
67+ showCryptoOnly, setShowCryptoOnly,
68+ sortBy, setSortBy,
69+ sortOrder, setSortOrder,
70+ } ,
71+ filteredAndSorted,
72+ activeFilterCount,
73+ hasActiveFilters : activeFilterCount > 0 ,
74+ clearAllFilters : ( ) => {
75+ setSearchQuery ( '' ) ;
76+ setSelectedCategories ( [ ] ) ;
77+ setSelectedBillingCycles ( [ ] ) ;
78+ setPriceRange ( { min : 0 , max : 1000 } ) ;
79+ setShowActiveOnly ( true ) ;
80+ setShowCryptoOnly ( false ) ;
81+ setSortBy ( 'name' ) ;
82+ setSortOrder ( 'asc' ) ;
83+ }
84+ } ;
85+ } ;
0 commit comments