1
1
"use client"
2
2
3
- import { useEffect , useState } from "react"
3
+ import { useEffect , useState , useRef , useMemo } from "react"
4
4
import { Button } from "@/components/ui/button"
5
5
import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from "@/components/ui/select"
6
6
import { exams , slots , years } from "@/components/select_options"
7
7
import { Input } from "@/components/ui/input"
8
8
import axios from "axios"
9
+ import Fuse from "fuse.js"
9
10
10
11
export default function PapersPage ( ) {
11
12
const [ subjects , setSubjects ] = useState < string [ ] > ( [ ] )
@@ -15,31 +16,34 @@ export default function PapersPage() {
15
16
const [ selectedExam , setSelectedExam ] = useState < string | null > ( null )
16
17
const [ selectedSlot , setSelectedSlot ] = useState < string | null > ( null )
17
18
const [ selectedYear , setSelectedYear ] = useState < string | null > ( null )
19
+ const suggestionsRef = useRef < HTMLUListElement | null > ( null )
18
20
19
21
useEffect ( ( ) => {
20
22
async function fetchSubjects ( ) {
21
23
try {
22
24
const response = await axios . get ( `${ process . env . NEXT_PUBLIC_SERVER_URL } /api/course-list` )
23
- setSubjects ( response . data . map ( ( course : { name : string } ) => course . name ) )
25
+ console . log ( "API subjects:" , response . data )
26
+ const names = response . data . map ( ( course : any ) => course . name || course . courseName || course . title )
27
+ setSubjects ( names )
24
28
} catch ( err ) {
25
29
console . error ( "Error fetching subjects:" , err )
26
30
}
27
31
}
28
32
fetchSubjects ( )
29
33
} , [ ] )
30
34
35
+ const fuse = useMemo ( ( ) => new Fuse ( subjects , { includeScore : true , threshold : 0.3 } ) , [ subjects ] )
31
36
32
37
useEffect ( ( ) => {
33
38
if ( ! searchText . trim ( ) ) {
34
39
setSuggestions ( [ ] )
35
40
return
36
41
}
37
- const lower = searchText . toLowerCase ( )
38
- const filtered = subjects . filter ( ( subj ) =>
39
- subj . toLowerCase ( ) . includes ( lower )
40
- )
41
- setSuggestions ( filtered . slice ( 0 , 10 ) )
42
- } , [ searchText , subjects ] )
42
+ const results = fuse . search ( searchText )
43
+ const mapped = results . map ( r => r . item ) . slice ( 0 , 10 )
44
+ console . log ( "Filtered suggestions:" , mapped )
45
+ setSuggestions ( mapped )
46
+ } , [ searchText , fuse ] )
43
47
44
48
const handleSelectSubject = ( subject : string ) => {
45
49
setSelectedSubject ( subject )
@@ -50,130 +54,67 @@ export default function PapersPage() {
50
54
setSelectedYear ( null )
51
55
}
52
56
57
+ useEffect ( ( ) => {
58
+ function handleClickOutside ( event : MouseEvent ) {
59
+ if ( suggestionsRef . current && ! suggestionsRef . current . contains ( event . target as Node ) ) {
60
+ setSuggestions ( [ ] )
61
+ }
62
+ }
63
+ document . addEventListener ( "mousedown" , handleClickOutside )
64
+ return ( ) => document . removeEventListener ( "mousedown" , handleClickOutside )
65
+ } , [ ] )
66
+
53
67
const handleSubmit = ( ) => {
54
68
if ( ! selectedSubject || ! selectedExam || ! selectedSlot || ! selectedYear ) {
55
69
alert ( "Please fill all fields before submitting" )
56
70
return
57
71
}
58
- console . log ( {
59
- subject : selectedSubject ,
60
- exam : selectedExam ,
61
- slot : selectedSlot ,
62
- year : selectedYear ,
63
- } )
64
-
72
+ console . log ( { subject : selectedSubject , exam : selectedExam , slot : selectedSlot , year : selectedYear } )
65
73
}
66
74
67
75
return (
68
76
< div className = "min-h-screen bg-[#F3F5FF] dark:bg-[#070114] text-black dark:text-white px-6 py-12" >
69
77
< main >
70
78
< div className = "max-w-4xl mx-auto text-center mb-16" >
71
- < h2 className = "font-vipnabd text-3xl md:text-4xl font-extrabold mb-12" >
72
- Specific Paper Request
73
- </ h2 >
74
-
75
- { }
79
+ < h2 className = "font-vipnabd text-3xl md:text-4xl font-extrabold mb-12" > Specific Paper Request</ h2 >
76
80
< div className = "relative max-w-xl mx-auto mb-8 font-play" >
77
81
< Input
78
82
type = "text"
79
83
value = { searchText }
80
84
onChange = { ( e ) => setSearchText ( e . target . value ) }
81
85
placeholder = "Search by subject..."
82
- className = { `text-md rounded-lg bg-[#B2B8FF] px-4 py-6 pr-10 font-play tracking-wider
83
- text-black shadow-sm ring-0 placeholder:text-black focus:outline-none focus:ring-0
84
- dark:bg-[#7480FF66] dark:text-white placeholder:dark:text-white
85
- ${ suggestions . length > 0 ? "rounded-b-none" : "" } ` }
86
+ className = { `text-md rounded-lg bg-[#B2B8FF] px-4 py-6 pr-10 font-play tracking-wider text-black shadow-sm ring-0 placeholder:text-black focus:outline-none focus:ring-0 dark:bg-[#7480FF66] dark:text-white placeholder:dark:text-white ${ suggestions . length > 0 ? "rounded-b-none" : "" } ` }
86
87
/>
87
- < button
88
- type = "button"
89
- className = "absolute inset-y-0 right-0 flex items-center pr-3"
90
- >
91
- < svg xmlns = "http://www.w3.org/2000/svg"
92
- className = "h-5 w-5 text-black dark:text-white"
93
- fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
94
- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2"
95
- d = "M21 21l-4.35-4.35M17 11a6 6 0 11-12 0 6 6 0 0112 0z" />
88
+ < button type = "button" className = "absolute inset-y-0 right-0 flex items-center pr-3" >
89
+ < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5 text-black dark:text-white" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
90
+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2" d = "M21 21l-4.35-4.35M17 11a6 6 0 11-12 0 6 6 0 0112 0z" />
96
91
</ svg >
97
92
</ button >
98
-
99
93
{ suggestions . length > 0 && (
100
- < ul
101
- className = { `absolute z-20 h-[250px] w-full max-w-xl overflow-y-scroll rounded-md
102
- rounded-t-none border border-t-0 bg-white text-center shadow-lg dark:bg-[#303771]
103
- ${ suggestions . length > 6 ? "h-[250px]" : "h-auto" }
104
- ${ suggestions . length > 10 ? "md:h-[400px]" : "md:h-auto" } ` }
105
- >
94
+ < ul ref = { suggestionsRef } className = "absolute z-20 max-h-[250px] w-full max-w-xl overflow-y-auto rounded-md rounded-t-none border border-t-0 bg-white text-center shadow-lg dark:bg-[#303771]" >
106
95
{ suggestions . map ( ( s , idx ) => (
107
- < li
108
- key = { idx }
109
- onClick = { ( ) => handleSelectSubject ( s ) }
110
- className = "cursor-pointer truncate p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
111
- >
96
+ < li key = { idx } onClick = { ( ) => handleSelectSubject ( s ) } className = "cursor-pointer truncate p-2 hover:bg-gray-100 dark:hover:bg-gray-800" >
112
97
{ s }
113
98
</ li >
114
99
) ) }
115
100
</ ul >
116
101
) }
117
102
</ div >
118
-
119
- { }
120
103
< div className = "flex justify-center gap-4 mb-8" >
121
- < Select
122
- onValueChange = { setSelectedExam }
123
- disabled = { ! selectedSubject }
124
- >
125
- < SelectTrigger className = "w-32" >
126
- < SelectValue placeholder = "Exam" />
127
- </ SelectTrigger >
128
- < SelectContent >
129
- { exams . map ( ( exam ) => (
130
- < SelectItem key = { exam } value = { exam } >
131
- { exam }
132
- </ SelectItem >
133
- ) ) }
134
- </ SelectContent >
104
+ < Select onValueChange = { setSelectedExam } disabled = { ! selectedSubject } >
105
+ < SelectTrigger className = "w-32" > < SelectValue placeholder = "Exam" /> </ SelectTrigger >
106
+ < SelectContent > { exams . map ( ( exam ) => < SelectItem key = { exam } value = { exam } > { exam } </ SelectItem > ) } </ SelectContent >
135
107
</ Select >
136
-
137
- < Select
138
- onValueChange = { setSelectedSlot }
139
- disabled = { ! selectedSubject }
140
- >
141
- < SelectTrigger className = "w-32" >
142
- < SelectValue placeholder = "Slot" />
143
- </ SelectTrigger >
144
- < SelectContent >
145
- { slots . map ( ( slot ) => (
146
- < SelectItem key = { slot } value = { slot } >
147
- { slot }
148
- </ SelectItem >
149
- ) ) }
150
- </ SelectContent >
108
+ < Select onValueChange = { setSelectedSlot } disabled = { ! selectedSubject } >
109
+ < SelectTrigger className = "w-32" > < SelectValue placeholder = "Slot" /> </ SelectTrigger >
110
+ < SelectContent > { slots . map ( ( slot ) => < SelectItem key = { slot } value = { slot } > { slot } </ SelectItem > ) } </ SelectContent >
151
111
</ Select >
152
-
153
- < Select
154
- onValueChange = { setSelectedYear }
155
- disabled = { ! selectedSubject }
156
- >
157
- < SelectTrigger className = "w-32" >
158
- < SelectValue placeholder = "Year" />
159
- </ SelectTrigger >
160
- < SelectContent >
161
- { years . map ( ( year ) => (
162
- < SelectItem key = { year } value = { year } >
163
- { year }
164
- </ SelectItem >
165
- ) ) }
166
- </ SelectContent >
112
+ < Select onValueChange = { setSelectedYear } disabled = { ! selectedSubject } >
113
+ < SelectTrigger className = "w-32" > < SelectValue placeholder = "Year" /> </ SelectTrigger >
114
+ < SelectContent > { years . map ( ( year ) => < SelectItem key = { year } value = { year } > { year } </ SelectItem > ) } </ SelectContent >
167
115
</ Select >
168
116
</ div >
169
-
170
- { }
171
- < Button
172
- className = "px-8 py-3 rounded-lg text-base bg-[#4A55FF] hover:bg-[#3A44CC] text-white dark:bg-[#9EA8FF] dark:hover:bg-[#7D86E5] dark:text-black"
173
- onClick = { handleSubmit }
174
- >
175
- Submit
176
- </ Button >
117
+ < Button className = "px-8 py-3 rounded-lg text-base bg-[#4A55FF] hover:bg-[#3A44CC] text-white dark:bg-[#9EA8FF] dark:hover:bg-[#7D86E5] dark:text-black" onClick = { handleSubmit } > Submit</ Button >
177
118
</ div >
178
119
</ main >
179
120
</ div >
0 commit comments