1+ "use client" ;
2+
3+ import React from 'react'
4+ import {
5+ Dialog ,
6+ DialogContent ,
7+ DialogDescription ,
8+ DialogHeader ,
9+ DialogTitle ,
10+ DialogTrigger ,
11+ } from "@/components/ui/dialog"
12+ import { ArrowUpRight } from 'lucide-react'
13+ import { Input } from "@/components/ui/input" ;
14+ import { useState , useRef , useEffect , useMemo } from 'react' ;
15+ import { Search } from "lucide-react" ;
16+ import {
17+ Select ,
18+ SelectContent ,
19+ SelectItem ,
20+ SelectTrigger ,
21+ SelectValue ,
22+ } from "@/components/ui/select" ;
23+ import { Button } from "@/components/ui/button" ;
24+ import axios from 'axios' ;
25+ import toast from 'react-hot-toast' ;
26+ import { exams , slots , years } from "@/components/select_options" ;
27+ import Fuse from 'fuse.js' ;
28+ import { useCourses } from "@/context/courseContext" ;
29+
30+ type Course = {
31+ name ?: string | null ;
32+ courseName ?: string | null ;
33+ title ?: string | null ;
34+ } ;
35+
36+ const RequestModal = ( ) => {
37+ const [ open , setOpen ] = useState ( false ) ;
38+ const [ subjects , setSubjects ] = useState < string [ ] > ( [ ] ) ;
39+ const [ searchText , setSearchText ] = useState ( "" ) ;
40+ const [ suggestions , setSuggestions ] = useState < string [ ] > ( [ ] ) ;
41+ const suggestionsRef = useRef < HTMLUListElement | null > ( null ) ;
42+ const [ selectedSubject , setSelectedSubject ] = useState < string | null > ( null ) ;
43+ const [ selectedExam , setSelectedExam ] = useState < string | null > ( null ) ;
44+ const [ selectedSlot , setSelectedSlot ] = useState < string | null > ( null ) ;
45+ const [ selectedYear , setSelectedYear ] = useState < string | null > ( null ) ;
46+ const { courses, loading, error, refetch } = useCourses ( ) ;
47+
48+ useEffect ( ( ) => {
49+ setSubjects ( courses . map ( ( course ) => course . name ) ) ;
50+ } , [ courses ] ) ;
51+
52+
53+ const resetModal = ( ) => {
54+ setSearchText ( "" ) ;
55+ setSelectedSubject ( null ) ;
56+ setSelectedExam ( null ) ;
57+ setSelectedSlot ( null ) ;
58+ setSelectedYear ( null ) ;
59+ setSuggestions ( [ ] ) ;
60+ } ;
61+
62+
63+ const handleSelectSubject = ( subject : string ) => {
64+ setSelectedSubject ( subject ) ;
65+ setSearchText ( subject ) ;
66+ setSuggestions ( [ ] ) ;
67+ setSelectedExam ( null ) ;
68+ setSelectedSlot ( null ) ;
69+ setSelectedYear ( null ) ;
70+ } ;
71+
72+ const handleSubmit = async ( ) => {
73+ if ( ! selectedSubject || ! selectedExam || ! selectedSlot || ! selectedYear ) {
74+ toast . error ( "Please fill all fields before submitting." ) ;
75+ return ;
76+ }
77+
78+ try {
79+ await toast . promise (
80+ axios . post ( "/api/request" , {
81+ subject : selectedSubject ,
82+ exam : selectedExam ,
83+ slot : selectedSlot ,
84+ year : selectedYear ,
85+ } ) ,
86+ {
87+ loading : "Submitting your request..." ,
88+ success : "Your paper request was submitted successfully" ,
89+ error : "Failed to submit your request. Please try again later." ,
90+ } ,
91+ ) ;
92+
93+ setOpen ( false ) ;
94+ } catch ( error ) {
95+ console . error ( "Error submitting request:" , error ) ;
96+ }
97+ } ;
98+ const fuse = useMemo (
99+ ( ) => new Fuse ( subjects , { includeScore : true , threshold : 0.3 } ) ,
100+ [ subjects ] ,
101+ ) ;
102+
103+ useEffect ( ( ) => {
104+ if ( ! searchText . trim ( ) ) {
105+ setSuggestions ( [ ] ) ;
106+ return ;
107+ }
108+
109+ if ( selectedSubject && searchText === selectedSubject ) {
110+ setSuggestions ( [ ] ) ;
111+ return ;
112+ }
113+ const results = fuse . search ( searchText ) ;
114+ setSuggestions ( results . map ( ( r ) => r . item ) . slice ( 0 , 10 ) ) ;
115+ } , [ searchText , fuse , selectedSubject ] ) ;
116+
117+ return (
118+ < Dialog open = { open } onOpenChange = { ( isOpen ) => {
119+ setOpen ( isOpen ) ;
120+ if ( isOpen ) resetModal ( ) ;
121+ } } >
122+ < DialogTrigger className = 'flex items-center gap-2' >
123+ < ArrowUpRight className = "h-4 w-4" />
124+ < span className = "font-medium" > Request Paper</ span >
125+ </ DialogTrigger >
126+ < DialogContent className = 'bg-[#F3F5FF] dark:bg-[#070114] border-[#3A3745] items-start' >
127+ < DialogHeader >
128+ < DialogTitle > Request Papers</ DialogTitle >
129+ < DialogDescription >
130+ Having trouble finding a specific paper? Don't worry! Simply submit a request here, and our team will track it down, source it, and upload it for you so you can access it hassle-free.
131+ </ DialogDescription >
132+ </ DialogHeader >
133+ < div className = "text-end" >
134+ < div className = "relative mx-auto mt-4 mb-8 max-w-xl font-play" >
135+ < Input
136+ type = "text"
137+ value = { searchText }
138+ onChange = { ( e ) => setSearchText ( e . target . value ) }
139+ placeholder = "Search by subject..."
140+ 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" : "" } ` }
141+ />
142+ < button
143+ type = "button"
144+ className = "absolute inset-y-0 right-0 flex items-center pr-3"
145+ >
146+ < Search className = "h-5 w-5 text-black dark:text-white" /> { " " }
147+ </ button >
148+ { suggestions . length > 0 && (
149+ < ul
150+ ref = { suggestionsRef }
151+ 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]"
152+ >
153+ { suggestions . map ( ( s , idx ) => (
154+ < li
155+ key = { idx }
156+ onClick = { ( ) => handleSelectSubject ( s ) }
157+ className = "cursor-pointer truncate p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
158+ >
159+ { s }
160+ </ li >
161+ ) ) }
162+ </ ul >
163+ ) }
164+ </ div >
165+
166+ < div className = "mb-8 flex justify-center gap-4 w-full" >
167+ < Select
168+ onValueChange = { setSelectedExam }
169+ disabled = { ! selectedSubject }
170+ value = { selectedExam ?? undefined }
171+ >
172+ < SelectTrigger className = "flex-1 dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]" >
173+ < SelectValue placeholder = "Exam" />
174+ </ SelectTrigger >
175+ < SelectContent className = 'dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]' >
176+ { exams . map ( ( exam ) => (
177+ < SelectItem key = { exam } value = { exam } className = 'cursor-pointer hover:bg-slate-50 dark:hover:bg-[#1A1823]' >
178+ { exam }
179+ </ SelectItem >
180+ ) ) }
181+ </ SelectContent >
182+ </ Select >
183+ < Select
184+ onValueChange = { setSelectedSlot }
185+ disabled = { ! selectedSubject }
186+ value = { selectedSlot ?? undefined }
187+ >
188+ < SelectTrigger className = "flex-1 dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]" >
189+ < SelectValue placeholder = "Slot" />
190+ </ SelectTrigger >
191+ < SelectContent className = 'dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]' >
192+ { slots . map ( ( slot ) => (
193+ < SelectItem key = { slot } value = { slot } className = 'cursor-pointer hover:bg-slate-50 dark:hover:bg-[#1A1823]' >
194+ { slot }
195+ </ SelectItem >
196+ ) ) }
197+ </ SelectContent >
198+ </ Select >
199+ < Select
200+ onValueChange = { setSelectedYear }
201+ disabled = { ! selectedSubject }
202+ value = { selectedYear ?? undefined }
203+ >
204+ < SelectTrigger className = "flex-1 dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]" >
205+ < SelectValue placeholder = "Year" />
206+ </ SelectTrigger >
207+ < SelectContent className = 'dark:bg-black dark:text-white border-[#3A3745] bg-[#e8e9ff]' >
208+ { [ ...years ]
209+ . sort ( ( a , b ) => Number ( b ) - Number ( a ) )
210+ . map ( ( year ) => (
211+ < SelectItem key = { year } value = { year } className = 'cursor-pointer hover:bg-slate-50 dark:hover:bg-[#1A1823]' >
212+ { year }
213+ </ SelectItem >
214+ ) ) }
215+ </ SelectContent >
216+ </ Select >
217+ </ div >
218+
219+ < Button
220+ className = "rounded-md px-8 py-3 hover:opacity-80 bg-[#B2B8FF] text-black dark:border-[#36266D] dark:bg-[#7480ff9d] dark:text-white"
221+ onClick = { handleSubmit }
222+ >
223+ Submit
224+ </ Button >
225+ </ div >
226+ </ DialogContent >
227+ </ Dialog >
228+ )
229+ }
230+
231+ export default RequestModal
0 commit comments