1
1
"use client" ;
2
+
2
3
import React , { useState } from "react" ;
3
4
import axios from "axios" ;
4
5
import toast from "react-hot-toast" ;
@@ -7,46 +8,26 @@ import { Button } from "@/components/ui/button";
7
8
import Navbar from "@/components/Navbar" ;
8
9
import Footer from "@/components/Footer" ;
9
10
import { type PostPDFToCloudinary } from "@/interface" ;
11
+ import { slots , years , campuses , semesters , exams } from "@/components/select_options" ;
12
+ import SearchBar from "@/components/searchbarSubjectList" ;
10
13
import Dropzone from "react-dropzone" ;
11
-
12
- import { createCanvas } from "canvas" ;
13
- import { getDocument , GlobalWorkerOptions } from "pdfjs-dist" ;
14
- import { PDFDocument } from "pdf-lib" ;
15
- async function pdfToImage ( file : File ) {
16
- GlobalWorkerOptions . workerSrc =
17
- "https://unpkg.com/[email protected] /build/pdf.worker.min.js" ;
18
-
19
- const pdfDoc = await PDFDocument . load ( await file . arrayBuffer ( ) ) ;
20
-
21
- // Get the first page
22
- const page = pdfDoc . getPages ( ) [ 0 ] ;
23
- if ( ! page ) {
24
- throw "First page not found" ;
25
- }
26
- // Create a canvas to render the image
27
- const canvas = createCanvas ( page . getWidth ( ) , page . getHeight ( ) ) ;
28
- const context = canvas . getContext ( "2d" ) ;
29
-
30
- // Use pdfjs-dist to render the page
31
- const pdfjsDoc = await getDocument ( { data : await file . arrayBuffer ( ) } )
32
- . promise ;
33
- const pdfPage = await pdfjsDoc . getPage ( 1 ) ;
34
-
35
- // Render page to canvas
36
- const viewport = pdfPage . getViewport ( { scale : 1 } ) ;
37
- await pdfPage . render ( { canvasContext : context , viewport } ) . promise ;
38
-
39
- // Convert the canvas to the desired output (Buffer, base64, etc.)
40
- return canvas . toDataURL ( ) ; // Returns a Base64 string
41
- }
14
+ import {
15
+ Select ,
16
+ SelectContent ,
17
+ SelectGroup ,
18
+ SelectItem ,
19
+ SelectLabel ,
20
+ SelectTrigger ,
21
+ SelectValue ,
22
+ } from "@/components/ui/select" ;
42
23
43
24
const Page = ( ) => {
25
+ const [ slot , setSlot ] = useState ( "" ) ;
26
+ const [ subject , setSubject ] = useState ( "" ) ;
27
+ const [ exam , setExam ] = useState ( "" ) ;
28
+ const [ year , setYear ] = useState ( "" ) ;
44
29
const [ campus , setCampus ] = useState ( "Vellore" ) ;
45
-
46
- const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
47
-
48
- const [ isUploading , setIsUploading ] = useState ( false ) ;
49
- const [ , setResetSearch ] = useState ( false ) ;
30
+ const [ semester , setSemester ] = useState ( "" ) ;
50
31
function fileCheckAndSelect < T extends File > ( acceptedFiles : T [ ] ) {
51
32
const maxFileSize = 5 * 1024 * 1024 ;
52
33
const allowedFileTypes = [
@@ -105,23 +86,82 @@ const Page = () => {
105
86
id : toastId ,
106
87
} ) ;
107
88
}
89
+ const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
90
+ const [ isUploading , setIsUploading ] = useState ( false ) ;
91
+ const [ resetSearch , setResetSearch ] = useState ( false ) ;
92
+
108
93
const handlePrint = async ( ) => {
94
+ const maxFileSize = 5 * 1024 * 1024 ;
95
+ const allowedFileTypes = [
96
+ "application/pdf" ,
97
+ "image/jpeg" ,
98
+ "image/png" ,
99
+ "image/gif" ,
100
+ ] ;
101
+
102
+ if ( ! slot ) {
103
+ toast . error ( "Slot is required" ) ;
104
+ return ;
105
+ }
106
+ if ( ! subject ) {
107
+ toast . error ( "Subject is required" ) ;
108
+ return ;
109
+ }
110
+ if ( ! exam ) {
111
+ toast . error ( "Exam is required" ) ;
112
+ return ;
113
+ }
114
+ if ( ! year ) {
115
+ toast . error ( "Year is required" ) ;
116
+ return ;
117
+ }
109
118
if ( ! campus ) {
110
119
setCampus ( "Vellore" ) ;
111
120
}
112
121
122
+ if ( ! semester ) {
123
+ toast . error ( "Semester is required" ) ;
124
+ return ;
125
+ }
126
+ if ( ! files || files . length === 0 ) {
127
+ toast . error ( "No files selected" ) ;
128
+ return ;
129
+ }
130
+
131
+ if ( files . length > 5 ) {
132
+ toast . error ( "More than 5 files selected" ) ;
133
+ return ;
134
+ }
135
+
136
+ // File validations
137
+ const invalidFiles = files . filter (
138
+ ( file ) =>
139
+ file . size > maxFileSize || ! allowedFileTypes . includes ( file . type ) ,
140
+ ) ;
141
+
142
+ if ( invalidFiles . length > 0 ) {
143
+ toast . error (
144
+ `Some files are invalid. Ensure each file is below 5MB and of an allowed type (PDF, JPEG, PNG, GIF).` ,
145
+ ) ;
146
+ return ;
147
+ }
148
+
113
149
const isPdf = files . length === 1 && files [ 0 ] ?. type === "application/pdf" ;
150
+ if ( isPdf && files . length > 1 ) {
151
+ toast . error ( "PDFs must be uploaded separately" ) ;
152
+ return ;
153
+ }
114
154
115
155
// Prepare FormData
116
156
const formData = new FormData ( ) ;
117
157
files . forEach ( ( file ) => {
118
158
formData . append ( "files" , file ) ;
119
159
} ) ;
120
-
121
- if ( isPdf && files [ 0 ] ) {
122
- formData . append ( "image " , await pdfToImage ( files [ 0 ] ) ) ;
123
- }
124
- // formData.append("exam ", exam );
160
+ formData . append ( "subject" , subject ) ;
161
+ formData . append ( "slot" , slot ) ;
162
+ formData . append ( "year " , year ) ;
163
+ formData . append ( "exam" , exam ) ;
164
+ formData . append ( "semester " , semester ) ;
125
165
formData . append ( "campus" , campus ) ;
126
166
127
167
formData . append ( "isPdf" , String ( isPdf ) ) ;
@@ -130,18 +170,18 @@ const Page = () => {
130
170
131
171
try {
132
172
await toast . promise (
133
- axios . post < PostPDFToCloudinary > ( "/api/ai- upload" , formData ) ,
173
+ axios . post < PostPDFToCloudinary > ( "/api/upload" , formData ) ,
134
174
{
135
175
loading : "Uploading papers..." ,
136
176
success : "Papers uploaded successfully!" ,
137
177
error : "Failed to upload papers. Please try again." ,
138
178
} ,
139
179
) ;
140
180
141
- // setSlot("");
142
- // setSubject("");
143
- // setExam("");
144
- // setYear("");
181
+ setSlot ( "" ) ;
182
+ setSubject ( "" ) ;
183
+ setExam ( "" ) ;
184
+ setYear ( "" ) ;
145
185
setFiles ( [ ] ) ;
146
186
setResetSearch ( true ) ;
147
187
setTimeout ( ( ) => setResetSearch ( false ) , 100 ) ;
@@ -159,14 +199,104 @@ const Page = () => {
159
199
</ div >
160
200
< div className = "2xl:my-15 flex flex-col items-center" >
161
201
< fieldset className = "mb-4 w-[350px] rounded-lg border-2 border-gray-300 p-4 pr-8" >
162
- { /* <legend className="text-lg font-bold">Upload papers </legend> */ }
202
+ < legend className = "text-lg font-bold" > Select paper parameters </ legend >
163
203
164
204
< div className = "flex w-full flex-col 2xl:gap-y-4" >
205
+ { /* Slot Selection */ }
206
+ < div >
207
+ < label > Slot:</ label >
208
+ < Select value = { slot } onValueChange = { setSlot } >
209
+ < SelectTrigger className = "m-2 rounded-md border p-2" >
210
+ < SelectValue placeholder = "Select slot" />
211
+ </ SelectTrigger >
212
+ < SelectContent >
213
+ < SelectGroup >
214
+ < SelectLabel > Slots</ SelectLabel >
215
+ { slots . map ( ( slot ) => (
216
+ < SelectItem key = { slot } value = { slot } >
217
+ { slot }
218
+ </ SelectItem >
219
+ ) ) }
220
+ </ SelectGroup >
221
+ </ SelectContent >
222
+ </ Select >
223
+ </ div >
224
+
225
+ { /* Exam Selection */ }
226
+ < div >
227
+ < label > Exam:</ label >
228
+ < Select value = { exam } onValueChange = { setExam } >
229
+ < SelectTrigger className = "m-2 rounded-md border p-2" >
230
+ < SelectValue placeholder = "Select exam" />
231
+ </ SelectTrigger >
232
+ < SelectContent >
233
+ < SelectGroup >
234
+ < SelectLabel > Exams</ SelectLabel >
235
+ { exams . map ( ( exam ) => (
236
+ < SelectItem key = { exam } value = { String ( exam ) } >
237
+ { exam }
238
+ </ SelectItem >
239
+ ) ) } { " " }
240
+ </ SelectGroup >
241
+ </ SelectContent >
242
+ </ Select >
243
+ </ div >
244
+
245
+ { /* Subject Selection */ }
246
+ < div >
247
+ < label > Subject:</ label >
248
+ < SearchBar setSubject = { setSubject } resetSearch = { resetSearch } />
249
+ </ div >
250
+
251
+ { /* Year Selection */ }
252
+ < div >
253
+ < label > Year:</ label >
254
+ < Select value = { year } onValueChange = { setYear } >
255
+ < SelectTrigger className = "m-2 rounded-md border p-2" >
256
+ < SelectValue placeholder = "Select year" />
257
+ </ SelectTrigger >
258
+ < SelectContent >
259
+ < SelectGroup >
260
+ < SelectLabel > Years</ SelectLabel >
261
+ { years . map ( ( year ) => (
262
+ < SelectItem key = { year } value = { String ( year ) } >
263
+ { year }
264
+ </ SelectItem >
265
+ ) ) }
266
+ </ SelectGroup >
267
+ </ SelectContent >
268
+ </ Select >
269
+ </ div >
270
+
271
+ { /* Year Selection */ }
272
+
273
+ < div >
274
+ < label > Semester Selection:</ label >
275
+ < Select value = { semester } onValueChange = { setSemester } >
276
+ < SelectTrigger className = "m-2 rounded-md border p-2" >
277
+ < SelectValue placeholder = "Select semester" />
278
+ </ SelectTrigger >
279
+ < SelectContent >
280
+ < SelectGroup >
281
+ < SelectLabel > Semester</ SelectLabel >
282
+ { semesters . map ( ( semester ) => (
283
+ < SelectItem key = { semester } value = { String ( semester ) } >
284
+ { semester }
285
+ </ SelectItem >
286
+ ) ) }
287
+ </ SelectGroup >
288
+ </ SelectContent >
289
+ </ Select >
290
+ </ div >
291
+
165
292
{ /* File Dropzone */ }
166
293
< div >
167
- < Dropzone onDrop = { fileCheckAndSelect } >
294
+ < Dropzone
295
+ onDrop = { ( acceptedFiles ) => setFiles ( acceptedFiles ) }
296
+ accept = { { "image/*" : [ ] , "application/pdf" : [ ] } }
297
+ >
168
298
{ ( { getRootProps, getInputProps } ) => (
169
- < section className = "my-2 -mr-2 cursor-pointer rounded-2xl border-2 border-dashed p-8 text-center" >
299
+ < section className = "my-2 -mr-2 rounded-2xl border-2 border-dashed p-8 text-center" >
170
300
< div { ...getRootProps ( ) } >
171
301
< input { ...getInputProps ( ) } />
172
302
< p >
@@ -207,4 +337,4 @@ const Page = () => {
207
337
) ;
208
338
} ;
209
339
210
- export default Page ;
340
+ export default Page ;
0 commit comments