11import React from 'react' ;
22import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
33import { faSearch , faBug , faLock , faUserSecret , faNetworkWired , faBrain , faTerminal } from '@fortawesome/free-solid-svg-icons' ;
4+ import { useState , useEffect } from 'react' ;
5+ import request from '@/utils/request' ;
6+ import { useRouter } from 'next/router' ;
47
58const SearchModal = ( { showSearchModal, setShowSearchModal } ) => {
9+ const [ search , setSearch ] = useState ( '' ) ;
10+ const [ results , setResults ] = useState ( null ) ;
11+ const router = useRouter ( ) ;
12+ const debouncedSearchTerm = useDebounce ( search , 100 ) ; // 300ms delay
13+
14+ useEffect ( ( ) => {
15+ if ( debouncedSearchTerm ) {
16+ const endpoint = process . env . NEXT_PUBLIC_API_URL ;
17+ const url = `${ endpoint } /search/${ debouncedSearchTerm } ` ;
18+ console . log ( url ) ;
19+ request ( url , "GET" , null ) . then ( ( res ) => {
20+ setResults ( res . results ) ;
21+ console . log ( res ) ;
22+ } ) . catch ( ( err ) => { console . log ( err ) ; } ) ;
23+ }
24+ } , [ debouncedSearchTerm ] ) ;
25+
26+ const routeToChallenge = ( id ) => router . push ( "/challenges/" + id ) ;
27+ const routeToUser = ( username ) => router . push ( "/users/" + username ) ;
28+
629 return (
730 < >
831 { showSearchModal && (
@@ -19,8 +42,31 @@ const SearchModal = ({ showSearchModal, setShowSearchModal }) => {
1942 < div className = "px-4 py-5 sm:px-6 mx-auto" >
2043 < div className = 'flex items-center mx-auto' >
2144 < FontAwesomeIcon icon = { faSearch } className = "w-5 h-5 mr-1 text-white" />
22- < input placeholder = "Search for challenges, users, or competitions" className = 'w-full border-0 text-xl focus:ring-0 bg-transparent text-white' autoFocus />
45+ < input onChange = { e => {
46+ setSearch ( e . target . value )
47+ if ( e . target . value === '' ) setResults ( null ) ;
48+ } } placeholder = "Search for challenges, users, or competitions" className = 'w-full border-0 text-xl focus:ring-0 bg-transparent text-white' autoFocus />
2349 </ div >
50+
51+ {
52+ results && search && (
53+ < div className = 'mt-5' >
54+ < div className = 'grid grid-cols-1 sm:grid-cols-2 gap-4' >
55+ { results . challenges . map ( ( result , index ) => (
56+ < div style = { { cursor : "pointer" } } key = { index } className = 'p-3 bg-neutral-700 rounded-lg' onClick = { ( ) => routeToChallenge ( result . id ) } >
57+ < h1 className = 'text-md text-white font-semibold' > { result . title } </ h1 >
58+ </ div >
59+ ) ) }
60+ { results . users . map ( ( result , index ) => (
61+ < div style = { { cursor : "pointer" } } key = { index } className = 'p-3 bg-neutral-700 rounded-lg' onClick = { ( ) => routeToUser ( result . username ) } >
62+ < h1 className = 'text-md text-white font-semibold' > { result . username } </ h1 >
63+ </ div >
64+ ) ) }
65+ </ div >
66+ </ div >
67+ )
68+ }
69+
2470 < h1 className = 'mt-10 text-xl text-white font-semibold mb-4' > Search by Category</ h1 >
2571 < div className = "flex flex-wrap gap-2" >
2672 < button className = "px-4 py-2 rounded bg-blue-500 bg-opacity-50 border border-blue-800 hover:brightness-110 text-white flex items-center gap-2" >
@@ -58,4 +104,20 @@ const SearchModal = ({ showSearchModal, setShowSearchModal }) => {
58104 ) ;
59105} ;
60106
107+ function useDebounce ( value , delay ) {
108+ const [ debouncedValue , setDebouncedValue ] = useState ( value ) ;
109+
110+ useEffect ( ( ) => {
111+ const handler = setTimeout ( ( ) => {
112+ setDebouncedValue ( value ) ;
113+ } , delay ) ;
114+
115+ return ( ) => {
116+ clearTimeout ( handler ) ;
117+ } ;
118+ } , [ value , delay ] ) ;
119+
120+ return debouncedValue ;
121+ }
122+
61123export default SearchModal ;
0 commit comments