1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useRef , useState } from 'react' ;
22import { useNavigate } from 'react-router-dom' ;
33import { useTranslation } from 'react-i18next' ;
44import api from '../utils/api' ;
@@ -23,18 +23,36 @@ function Dashboard({onLogout, triggerLogout, setTriggerLogout}: DashboardProps)
2323 const [ , setCurrentBook ] = useState < BookShelfEntity | null > ( null ) ;
2424 const [ filterStatus , setFilterStatus ] = useState ( - 1 )
2525 const [ filteredBooks , setFilteredBooks ] = useState < BookShelfEntity [ ] > ( [ ] ) ;
26+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
27+ const searchInputRef = useRef < HTMLInputElement > ( null ) ;
2628
2729 useEffect ( ( ) => {
2830 loadBookshelf ( ) ;
31+
32+ const handleKeyDown = ( e : KeyboardEvent ) => {
33+ if ( ( e . ctrlKey || e . metaKey ) && e . key === 'k' ) {
34+ e . preventDefault ( ) ;
35+ searchInputRef . current ?. focus ( ) ;
36+ }
37+ } ;
38+ window . addEventListener ( 'keydown' , handleKeyDown ) ;
39+ return ( ) => window . removeEventListener ( 'keydown' , handleKeyDown ) ;
2940 } , [ ] ) ;
3041
3142 useEffect ( ( ) => {
3243 if ( books . length === 0 ) return ;
3344 let _filterStatus = filterStatus === - 1 ? [ 1 , 2 ] : [ filterStatus ] ;
34- setFilteredBooks (
35- books . filter ( book => _filterStatus . includes ( + book . status ) )
36- ) ;
37- } , [ filterStatus , books ] )
45+ let result = books . filter ( book => _filterStatus . includes ( + book . status ) ) ;
46+ if ( searchQuery . trim ( ) ) {
47+ const q = searchQuery . toLowerCase ( ) ;
48+ result = result . filter ( book =>
49+ book . book ?. name ?. toLowerCase ( ) . includes ( q ) ||
50+ book . book ?. authorsAsString ?. toLowerCase ( ) . includes ( q ) ||
51+ book . abook ?. narratorAsString ?. toLowerCase ( ) . includes ( q )
52+ ) ;
53+ }
54+ setFilteredBooks ( result ) ;
55+ } , [ filterStatus , books , searchQuery ] )
3856
3957 const loadBookshelf = async ( ) => {
4058 try {
@@ -88,6 +106,27 @@ function Dashboard({onLogout, triggerLogout, setTriggerLogout}: DashboardProps)
88106 </ div >
89107 ) : (
90108 < >
109+ < div className = "relative mb-4" >
110+ < svg className = "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
111+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z" />
112+ </ svg >
113+ < input
114+ ref = { searchInputRef }
115+ type = "text"
116+ value = { searchQuery }
117+ onChange = { e => setSearchQuery ( e . target . value ) }
118+ placeholder = { t ( 'dashboard.search' ) }
119+ className = "w-full bg-gray-900 border border-gray-700 text-white placeholder-gray-500 rounded-lg pl-10 pr-10 py-2 text-sm focus:outline-none focus:border-gray-500"
120+ />
121+ { searchQuery && (
122+ < button
123+ onClick = { ( ) => setSearchQuery ( '' ) }
124+ className = "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white"
125+ >
126+ ×
127+ </ button >
128+ ) }
129+ </ div >
91130
92131 < div className = "flex flex-wrap gap-3 mb-6" >
93132 { filterStatus !== - 1 && (
0 commit comments