1
1
"use client" ;
2
2
3
- import { useSearchParams , useRouter } from "next/navigation" ;
4
- import { useEffect , useState } from "react" ;
5
- import axios , { type AxiosError } from "axios" ;
6
- import Cryptr from "cryptr" ;
3
+ import CatalogueContent from "@/components/CatalogueContent" ;
7
4
import { Suspense } from "react" ;
8
- import Image from "next/image" ;
9
- import { Search , Download , Eye , Filter } from "lucide-react" ;
10
- import { boolean } from "zod" ;
11
- import {
12
- Dialog ,
13
- DialogContent ,
14
- DialogDescription ,
15
- DialogHeader ,
16
- DialogTitle ,
17
- DialogTrigger ,
18
- } from "@/components/ui/dialog" ;
19
-
20
- import { MultiSelect } from "@/components/multi-select" ;
21
- import { Button } from "@/components/ui/button" ;
22
- import { slots } from "../upload/select_options" ;
23
-
24
- interface Paper {
25
- _id : string ;
26
- exam : string ;
27
- finalUrl : string ;
28
- thumbnailUrl : string ;
29
- slot : string ;
30
- subject : string ;
31
- year : string ;
32
- }
33
-
34
- interface Filters {
35
- paper : Paper ;
36
- uniqueExams : string [ ] ;
37
- uniqueSlots : string [ ] ;
38
- uniqueYears : string [ ] ;
39
- }
40
- const cryptr = new Cryptr (
41
- process . env . NEXT_PUBLIC_CRYPTO_SECRET ?? "default_crypto_secret" ,
42
- ) ;
43
-
44
- const CatalogueContent = ( ) => {
45
- const router = useRouter ( ) ;
46
- const searchParams = useSearchParams ( ) ;
47
- const subject = searchParams . get ( "subject" ) ;
48
- const exams = searchParams . get ( "exams" ) ?. split ( "," ) ;
49
- const slots = searchParams . get ( "slots" ) ?. split ( "," ) ;
50
- const years = searchParams . get ( "years" ) ?. split ( "," ) ;
51
-
52
- const [ papers , setPapers ] = useState < Paper [ ] > ( [ ] ) ;
53
- const [ error , setError ] = useState < string | null > ( null ) ;
54
- const [ loading , setLoading ] = useState < boolean > ( false ) ;
55
- const [ filterOptions , setFilterOptions ] = useState < Filters > ( ) ;
56
-
57
- useEffect ( ( ) => {
58
- if ( subject ) {
59
- const fetchPapers = async ( ) => {
60
- setLoading ( true ) ;
61
-
62
- try {
63
- console . log ( subject )
64
- const papersResponse = await axios . get ( "/api/papers" , {
65
- // Digital Logic and Microprocessors[BITE202L]
66
- params : { subject } ,
67
- } ) ;
68
- const { res : encryptedPapersResponse } = papersResponse . data ;
69
- const decryptedPapersResponse = cryptr . decrypt (
70
- encryptedPapersResponse ,
71
- ) ;
72
- const papersData : Paper [ ] = JSON . parse (
73
- decryptedPapersResponse ,
74
- ) . papers ;
75
- const filters : Filters = JSON . parse ( decryptedPapersResponse ) ;
76
- setFilterOptions ( filters ) ;
77
- const papersDataWithFilters = papersData
78
- . filter ( ( paper ) => {
79
- const examCondition = exams && exams . length ? exams . includes ( paper . exam ) : true ;
80
- const slotCondition = slots && slots . length ? slots . includes ( paper . slot ) : true ;
81
- const yearCondition = years && years . length ? years . includes ( paper . year ) : true ;
82
-
83
- return examCondition && slotCondition && yearCondition ;
84
- } )
85
-
86
- if ( papersDataWithFilters . length > 0 )
87
- // setPapers(papersData);
88
-
89
- setPapers ( papersDataWithFilters ) ;
90
- else
91
- setPapers ( papersData ) ;
92
- } catch ( error ) {
93
- if ( axios . isAxiosError ( error ) ) {
94
- const axiosError = error as AxiosError < { message ?: string } > ;
95
- const errorMessage =
96
- axiosError . response ?. data ?. message ?? "Error fetching papers" ;
97
- setError ( errorMessage ) ;
98
- } else {
99
- setError ( "Error fetching papers" ) ;
100
- }
101
- } finally {
102
- setLoading ( false ) ;
103
- }
104
- } ;
105
-
106
- void fetchPapers ( ) ;
107
- }
108
- } , [ subject , searchParams ] ) ;
109
- // console.log(papers);
110
-
111
- return (
112
- < div className = "min-h-screen bg-gray-50 p-8" >
113
- < div className = "flex mb-4 mx-40 justify-center gap-10 items-center" >
114
- { /* <button
115
- onClick={() => router.push("/")}
116
- className=" rounded-md bg-blue-500 px-4 py-2 text-white"
117
- >
118
- Back to Search
119
- </button> */ }
120
- < div className = "relative w-full" >
121
- < div className = "absolute flex-grow flex top-3 left-2" >
122
- < Search className = "w-5 text-gray-400" />
123
- </ div >
124
- < input
125
- type = "search"
126
- className = "border w-full px-10 py-3 bg-[#7480FF33] bg-opacity-20 rounded-2xl "
127
- placeholder = "Search..."
128
- > </ input >
129
- </ div >
130
- { subject && filterOptions && < FilterDialog subject = { subject } filterOptions = { filterOptions } /> }
131
- </ div >
132
- { /* <h1 className="mb-4 text-2xl font-bold">Papers for {subject}</h1> */ }
133
- { error && < p className = "text-red-500" > { error } </ p > }
134
-
135
- { loading ? (
136
- < p > Loading papers...</ p >
137
- ) : papers . length > 0 ? (
138
- < div className = "flex flex-wrap gap-10" >
139
- { papers . map ( ( paper ) => (
140
- < Card key = { paper . _id } paper = { paper } />
141
- ) ) }
142
- </ div >
143
- ) : (
144
- < p > No papers available for this subject.</ p >
145
- ) }
146
- </ div >
147
- ) ;
148
- } ;
149
5
150
6
const Catalogue = ( ) => {
151
7
return (
@@ -154,154 +10,5 @@ const Catalogue = () => {
154
10
</ Suspense >
155
11
) ;
156
12
} ;
157
- export default Catalogue ;
158
-
159
- function Card ( { paper } : { paper : Paper } ) {
160
- const [ checked , setChecked ] = useState < boolean > ( false ) ;
161
- function handleCheckboxChange ( ) : void {
162
- setChecked ( ! checked ) ;
163
- }
164
-
165
- return (
166
- < div
167
- key = { paper . _id }
168
- className = { `space-y-1 w-56 rounded-md border border-black border-opacity-50 ${ checked ? "bg-[#EEF2FF]" : "bg-white" } p-4 ` }
169
- >
170
- < Image
171
- src = { paper . thumbnailUrl }
172
- alt = { paper . subject }
173
- // layout="responsive"
174
- width = { 320 } // Adjust width to maintain aspect ratio if needed
175
- height = { 180 } // Fixed height
176
- className = "mb-2 h-[180px] w-full object-cover" // Ensure it takes the full width of the container
177
- > </ Image >
178
- < div className = "text-sm font-medium" >
179
- { extractBracketContent ( paper . subject ) }
180
- </ div >
181
- < div className = "text-md font-medium" >
182
- { extractWithoutBracketContent ( paper . subject ) }
183
- </ div >
184
- { /* <p className="text-lg">{extractWithoutBracketContent(paper.subject)}</p> */ }
185
- < div className = "mb-2 flex gap-2 " >
186
- { capsule ( paper . exam ) }
187
- { capsule ( paper . slot ) }
188
- { capsule ( paper . year ) }
189
- </ div >
190
- < div className = "mt-5 flex items-center justify-between gap-2 " >
191
- < div className = "flex items-center gap-1" >
192
- < input
193
- onChange = { handleCheckboxChange }
194
- className = "h-3 w-3 rounded-lg"
195
- type = "checkbox"
196
- />
197
- < p className = "text-sm" > Select</ p >
198
- </ div >
199
- < div className = "flex gap-2" >
200
- < a
201
- href = { paper . finalUrl }
202
- target = "_blank"
203
- rel = "noopener noreferrer"
204
- // className="text-blue-500 hover:underline"
205
- >
206
- < Eye />
207
- </ a >
208
- < a href = { paper . finalUrl } download >
209
- < Download />
210
- </ a >
211
- </ div >
212
- </ div >
213
- </ div >
214
- ) ;
215
- }
216
-
217
- function capsule ( data : string ) {
218
- return (
219
- < div className = " rounded-md bg-[#7480FF] p-1 px-3 text-sm" > { data } </ div >
220
- ) ;
221
- }
222
- const FilterDialog = ( { subject, filterOptions} : { subject :string , filterOptions : Filters } ) => {
223
- const router = useRouter ( )
224
- const [ filterExams , setuniqueExams ] = useState < string [ ] > ( ) ;
225
- const [ FilterSlots , setFilterSlots ] = useState < string [ ] > ( ) ;
226
- const [ filterYears , setFilterYears ] = useState < string [ ] > ( ) ;
227
- const handleFilterClick = ( ) => {
228
- if ( subject ) {
229
- let pushContent = "/catalogue"
230
- if ( subject )
231
- {
232
- pushContent = pushContent . concat ( `?subject=${ encodeURIComponent ( subject ) } ` )
233
- }
234
- if ( filterExams )
235
- {
236
- pushContent = pushContent . concat ( `&exams=${ encodeURIComponent ( filterExams . join ( ',' ) ) } ` )
237
- }
238
- if ( FilterSlots )
239
- {
240
- pushContent = pushContent . concat ( `&slots=${ encodeURIComponent ( FilterSlots . join ( ',' ) ) } ` )
241
- }
242
- if ( filterYears )
243
- {
244
- pushContent = pushContent . concat ( `&years=${ encodeURIComponent ( filterYears . join ( ',' ) ) } ` )
245
-
246
- }
247
- router . push ( pushContent ) ;
248
- }
249
- } ;
250
-
251
- return (
252
- < Dialog >
253
- < DialogTrigger className = "rounded-lg bg-[#7480FF] px-8 py-3 text-white" >
254
- < div className = "flex gap-3" > Filter < Filter className = "" /> </ div >
255
- </ DialogTrigger >
256
- < DialogContent >
257
- < DialogHeader >
258
- < DialogTitle className = "mb-5" > Choose your filters</ DialogTitle >
259
- < DialogDescription className = "space-y-5" >
260
- { filterOptions && (
261
- < div className = "space-y-5" >
262
- < MultiSelect
263
- options = { filterOptions . uniqueExams . map ( ( exam : string ) => ( {
264
- label : exam ,
265
- value : exam ,
266
- } ) ) }
267
- onValueChange = { setuniqueExams }
268
- placeholder = "Exam"
269
- />
270
- < MultiSelect
271
- options = { filterOptions . uniqueSlots . map ( ( slots : string ) => ( {
272
- label : slots ,
273
- value : slots ,
274
- } ) ) }
275
- onValueChange = { setFilterSlots }
276
- placeholder = "Slots"
277
- />
278
- < MultiSelect
279
- options = { filterOptions . uniqueYears . map ( ( years : string ) => ( {
280
- label : years ,
281
- value : years ,
282
- } ) ) }
283
- onValueChange = { setFilterYears }
284
- placeholder = "Years"
285
- />
286
- </ div >
287
- ) }
288
- < Button variant = "outline" onClick = { handleFilterClick } >
289
- Filter
290
- </ Button >
291
- </ DialogDescription >
292
- </ DialogHeader >
293
- </ DialogContent >
294
- </ Dialog >
295
- ) ;
296
- } ;
297
-
298
-
299
- function extractBracketContent ( subject : string ) : string | null {
300
- const match = subject . match ( / \[ ( .* ?) \] / ) ;
301
- return match && match [ 1 ] ? match [ 1 ] : "BMAT102L" ; //MAKE SURE IT WORKS WHEN URL IS DONE FROM BACKEND
302
- }
303
-
304
- function extractWithoutBracketContent ( subject : string ) : string {
305
- return subject . replace ( / \s * \[ .* ?\] \s * / g, "" ) . trim ( ) ;
306
- }
307
13
14
+ export default Catalogue ;
0 commit comments