1
1
"use client" ;
2
- import React , { useState } from "react" ;
2
+ import React , { useState , useEffect } from "react" ;
3
3
import axios , { AxiosError } from "axios" ;
4
4
import toast from "react-hot-toast" ;
5
5
import { handleAPIError } from "../../util/error" ;
6
6
import { Button } from "@/components/ui/button" ;
7
7
8
8
import { type APIResponse } from "@/interface" ;
9
9
import Dropzone from "react-dropzone" ;
10
+ import { Upload } from "lucide-react" ;
10
11
11
12
const Page = ( ) => {
12
13
const [ campus , setCampus ] = useState ( "Vellore" ) ;
13
-
14
14
const [ files , setFiles ] = useState < File [ ] > ( [ ] ) ;
15
-
16
15
const [ isUploading , setIsUploading ] = useState ( false ) ;
17
16
const [ , setResetSearch ] = useState ( false ) ;
17
+ const [ isDragging , setIsDragging ] = useState ( false ) ;
18
+ const [ isGlobalDragging , setIsGlobalDragging ] = useState ( false ) ;
19
+
20
+ useEffect ( ( ) => {
21
+ const handleDragEnter = ( e : DragEvent ) => {
22
+ e . preventDefault ( ) ;
23
+ e . stopPropagation ( ) ;
24
+ setIsGlobalDragging ( true ) ;
25
+ } ;
26
+
27
+ const handleDragOver = ( e : DragEvent ) => {
28
+ e . preventDefault ( ) ;
29
+ e . stopPropagation ( ) ;
30
+ } ;
31
+
32
+ const handleDragLeave = ( e : DragEvent ) => {
33
+ e . preventDefault ( ) ;
34
+ e . stopPropagation ( ) ;
35
+
36
+ if (
37
+ ! e . relatedTarget ||
38
+ ( e . currentTarget !== e . relatedTarget &&
39
+ ! ( e . currentTarget as Element ) ?. contains ( e . relatedTarget as Node ) )
40
+ ) {
41
+ setIsGlobalDragging ( false ) ;
42
+ }
43
+ } ;
44
+
45
+ const handleDrop = ( e : DragEvent ) => {
46
+ e . preventDefault ( ) ;
47
+ e . stopPropagation ( ) ;
48
+ setIsGlobalDragging ( false ) ;
49
+ } ;
50
+
51
+ document . addEventListener ( "dragenter" , handleDragEnter ) ;
52
+ document . addEventListener ( "dragover" , handleDragOver ) ;
53
+ document . addEventListener ( "dragleave" , handleDragLeave ) ;
54
+ document . addEventListener ( "drop" , handleDrop ) ;
55
+
56
+ return ( ) => {
57
+ document . removeEventListener ( "dragenter" , handleDragEnter ) ;
58
+ document . removeEventListener ( "dragover" , handleDragOver ) ;
59
+ document . removeEventListener ( "dragleave" , handleDragLeave ) ;
60
+ document . removeEventListener ( "drop" , handleDrop ) ;
61
+ } ;
62
+ } , [ ] ) ;
63
+
18
64
function fileCheckAndSelect < T extends File > ( acceptedFiles : T [ ] ) {
19
65
const maxFileSize = 5 * 1024 * 1024 ;
20
66
const allowedFileTypes = [
@@ -39,7 +85,6 @@ const Page = () => {
39
85
return ;
40
86
}
41
87
42
- // File validations
43
88
const invalidFiles = acceptedFiles . filter (
44
89
( file ) =>
45
90
file . size > maxFileSize || ! allowedFileTypes . includes ( file . type ) ,
@@ -73,22 +118,20 @@ const Page = () => {
73
118
id : toastId ,
74
119
} ) ;
75
120
}
121
+
76
122
const handlePrint = async ( ) => {
77
123
if ( ! campus ) {
78
124
setCampus ( "Vellore" ) ;
79
125
}
80
126
81
127
const isPdf = files . length === 1 && files [ 0 ] ?. type === "application/pdf" ;
82
128
83
- // Prepare FormData
84
129
const formData = new FormData ( ) ;
85
130
files . forEach ( ( file ) => {
86
131
formData . append ( "files" , file ) ;
87
132
} ) ;
88
133
89
- // formData.append("exam", exam);
90
134
formData . append ( "campus" , campus ) ;
91
-
92
135
formData . append ( "isPdf" , String ( isPdf ) ) ;
93
136
94
137
setIsUploading ( true ) ;
@@ -117,10 +160,6 @@ const Page = () => {
117
160
} ,
118
161
) ;
119
162
120
- // setSlot("");
121
- // setSubject("");
122
- // setExam("");
123
- // setYear("");
124
163
setFiles ( [ ] ) ;
125
164
setResetSearch ( true ) ;
126
165
setTimeout ( ( ) => setResetSearch ( false ) , 100 ) ;
@@ -131,34 +170,59 @@ const Page = () => {
131
170
}
132
171
} ;
133
172
173
+ const isCurrentlyDragging = isDragging || isGlobalDragging ;
174
+
134
175
return (
135
- < div className = "play flex h-screen flex-col justify-center" >
176
+ < div className = "play flex h-[calc(100vh-85px)] flex-col justify-center px-6 " >
136
177
< div className = "2xl:my-15 flex flex-col items-center" >
137
178
< fieldset className = "mb-4 w-full max-w-md rounded-lg border-2 border-gray-300 p-4 pr-8" >
138
- { /* <legend className="text-lg font-bold">Upload papers</legend> */ }
139
-
140
179
< div className = "flex w-full flex-col 2xl:gap-y-4" >
141
180
{ /* File Dropzone */ }
142
181
< div >
143
- < Dropzone onDrop = { fileCheckAndSelect } >
182
+ < Dropzone
183
+ onDrop = { fileCheckAndSelect }
184
+ onDragEnter = { ( ) => setIsDragging ( true ) }
185
+ onDragLeave = { ( ) => setIsDragging ( false ) }
186
+ onDropAccepted = { ( ) => {
187
+ setIsDragging ( false ) ;
188
+ setIsGlobalDragging ( false ) ;
189
+ } }
190
+ onDropRejected = { ( ) => {
191
+ setIsDragging ( false ) ;
192
+ setIsGlobalDragging ( false ) ;
193
+ } }
194
+ >
144
195
{ ( { getRootProps, getInputProps } ) => (
145
196
< section
146
197
{ ...getRootProps ( ) }
147
- className = "my-2 -mr-2 cursor-pointer rounded-2xl border-2 border-dashed p-8 text-center"
198
+ className = { `my-2 -mr-2 cursor-pointer rounded-2xl border-2 ${
199
+ isCurrentlyDragging
200
+ ? "border-solid border-[#6D28D9] bg-purple-50 dark:bg-[#130E1F]"
201
+ : "border-dashed border-gray-300"
202
+ } p-8 text-center transition-all duration-200`}
148
203
>
149
- < input { ...getInputProps ( ) } />
204
+ < input { ...getInputProps ( ) } />
205
+ { isCurrentlyDragging ? (
206
+ < div className = "flex flex-col items-center" >
207
+ < p className = "text-lg font-medium text-[#6D28D9]" >
208
+ Drop files here
209
+ </ p >
210
+ < Upload className = "mt-2 h-10 w-10 animate-bounce text-[#6D28D9]" />
211
+ </ div >
212
+ ) : (
150
213
< p >
151
214
Drag 'n' drop some files here, or{ " " }
152
215
< span className = "text-[#6D28D9]" > click</ span > to select
153
216
files
154
217
</ p >
155
- < div
156
- className = { `mt-2 text-xs ${
157
- files ?. length === 0 ? "text-red-500" : "text-gray-600"
158
- } `}
159
- >
160
- { files ?. length || 0 } files selected
161
- </ div >
218
+ ) }
219
+ < div
220
+ className = { `mt-2 text-xs ${
221
+ files ?. length === 0 ? "text-red-500" : "text-gray-600"
222
+ } `}
223
+ >
224
+ { files ?. length || 0 } files selected
225
+ </ div >
162
226
</ section >
163
227
) }
164
228
</ Dropzone >
@@ -171,8 +235,10 @@ const Page = () => {
171
235
</ fieldset >
172
236
< Button
173
237
onClick = { handlePrint }
174
- disabled = { isUploading }
175
- className = { `w-fit rounded-md px-4 py-3 text-xl ${ isUploading ? "bg-gray-300" : "" } ` }
238
+ disabled = { isUploading || files . length === 0 }
239
+ className = { `w-fit rounded-md px-4 py-3 text-base ${
240
+ isUploading || files . length === 0 ? "opacity-60" : ""
241
+ } `}
176
242
>
177
243
{ isUploading ? "Uploading..." : "Upload Papers" }
178
244
</ Button >
0 commit comments