Seth0330 commited on
Commit
4ad4050
·
verified ·
1 Parent(s): 550be99

Create frontend/src/components/ocr/UploadZone.jsx

Browse files
frontend/src/components/ocr/UploadZone.jsx ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { motion, AnimatePresence } from "framer-motion";
3
+ import { Upload, FileText, Image, FileSpreadsheet, X, Sparkles } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ export default function UploadZone({ onFileSelect, selectedFile, onClear }) {
7
+ const [isDragging, setIsDragging] = useState(false);
8
+
9
+ const handleDragOver = (e) => {
10
+ e.preventDefault();
11
+ setIsDragging(true);
12
+ };
13
+
14
+ const handleDragLeave = () => {
15
+ setIsDragging(false);
16
+ };
17
+
18
+ const handleDrop = (e) => {
19
+ e.preventDefault();
20
+ setIsDragging(false);
21
+ const file = e.dataTransfer.files[0];
22
+ if (file) onFileSelect(file);
23
+ };
24
+
25
+ const getFileIcon = (type) => {
26
+ if (type?.includes("image")) return Image;
27
+ if (type?.includes("spreadsheet") || type?.includes("excel")) return FileSpreadsheet;
28
+ return FileText;
29
+ };
30
+
31
+ const FileIcon = selectedFile ? getFileIcon(selectedFile.type) : FileText;
32
+
33
+ return (
34
+ <div className="w-full">
35
+ <AnimatePresence mode="wait">
36
+ {!selectedFile ? (
37
+ <motion.div
38
+ key="upload"
39
+ initial={{ opacity: 0, y: 10 }}
40
+ animate={{ opacity: 1, y: 0 }}
41
+ exit={{ opacity: 0, y: -10 }}
42
+ transition={{ duration: 0.2 }}
43
+ onDragOver={handleDragOver}
44
+ onDragLeave={handleDragLeave}
45
+ onDrop={handleDrop}
46
+ className={cn(
47
+ "relative group cursor-pointer",
48
+ "border-2 border-dashed rounded-2xl",
49
+ "transition-all duration-300 ease-out",
50
+ isDragging
51
+ ? "border-indigo-400 bg-indigo-50/50"
52
+ : "border-slate-200 hover:border-indigo-300 hover:bg-slate-50/50"
53
+ )}
54
+ >
55
+ <label className="flex flex-col items-center justify-center py-16 px-8 cursor-pointer">
56
+ <motion.div
57
+ animate={isDragging ? { scale: 1.1, y: -5 } : { scale: 1, y: 0 }}
58
+ className={cn(
59
+ "h-16 w-16 rounded-2xl flex items-center justify-center mb-6 transition-colors duration-300",
60
+ isDragging
61
+ ? "bg-indigo-100"
62
+ : "bg-gradient-to-br from-slate-100 to-slate-50 group-hover:from-indigo-100 group-hover:to-violet-50"
63
+ )}
64
+ >
65
+ <Upload
66
+ className={cn(
67
+ "h-7 w-7 transition-colors duration-300",
68
+ isDragging ? "text-indigo-600" : "text-slate-400 group-hover:text-indigo-500"
69
+ )}
70
+ />
71
+ </motion.div>
72
+
73
+ <div className="text-center">
74
+ <p className="text-lg font-semibold text-slate-700 mb-1">
75
+ {isDragging ? "Drop your file here" : "Drop your file here, or browse"}
76
+ </p>
77
+ <p className="text-sm text-slate-400">
78
+ Supports PDF, PNG, JPG, TIFF, DOCX up to 50MB
79
+ </p>
80
+ </div>
81
+
82
+ <div className="flex items-center gap-2 mt-6">
83
+ <div className="flex -space-x-1">
84
+ {[
85
+ "bg-red-100 text-red-600",
86
+ "bg-blue-100 text-blue-600",
87
+ "bg-green-100 text-green-600",
88
+ "bg-amber-100 text-amber-600",
89
+ ].map((color, i) => (
90
+ <div
91
+ key={i}
92
+ className={`h-8 w-8 rounded-lg ${color.split(" ")[0]} flex items-center justify-center border-2 border-white`}
93
+ >
94
+ <FileText className={`h-4 w-4 ${color.split(" ")[1]}`} />
95
+ </div>
96
+ ))}
97
+ </div>
98
+ <span className="text-xs text-slate-400 ml-2">Multiple formats supported</span>
99
+ </div>
100
+
101
+ <input
102
+ type="file"
103
+ className="hidden"
104
+ accept=".pdf,.png,.jpg,.jpeg,.tiff,.docx,.xlsx"
105
+ onChange={(e) => e.target.files[0] && onFileSelect(e.target.files[0])}
106
+ />
107
+ </label>
108
+
109
+ {/* Decorative gradient border on hover */}
110
+ <div className="absolute inset-0 -z-10 rounded-2xl bg-gradient-to-r from-indigo-500 via-violet-500 to-purple-500 opacity-0 group-hover:opacity-10 blur-xl transition-opacity duration-500" />
111
+ </motion.div>
112
+ ) : (
113
+ <motion.div
114
+ key="selected"
115
+ initial={{ opacity: 0, scale: 0.95 }}
116
+ animate={{ opacity: 1, scale: 1 }}
117
+ exit={{ opacity: 0, scale: 0.95 }}
118
+ className="relative bg-gradient-to-br from-indigo-50 to-violet-50 rounded-2xl p-6 border border-indigo-100"
119
+ >
120
+ <div className="flex items-center gap-4">
121
+ <div className="h-14 w-14 rounded-xl bg-white shadow-sm flex items-center justify-center">
122
+ <FileIcon className="h-7 w-7 text-indigo-600" />
123
+ </div>
124
+ <div className="flex-1 min-w-0">
125
+ <p className="font-semibold text-slate-800 truncate">{selectedFile.name}</p>
126
+ <p className="text-sm text-slate-500">
127
+ {(selectedFile.size / 1024 / 1024).toFixed(2)} MB
128
+ </p>
129
+ </div>
130
+ <button
131
+ onClick={onClear}
132
+ className="h-9 w-9 rounded-xl bg-white hover:bg-red-50 border border-slate-200 hover:border-red-200 flex items-center justify-center text-slate-400 hover:text-red-500 transition-colors"
133
+ >
134
+ <X className="h-4 w-4" />
135
+ </button>
136
+ </div>
137
+
138
+ <div className="mt-4 flex items-center gap-2 text-sm">
139
+ <Sparkles className="h-4 w-4 text-indigo-500" />
140
+ <span className="text-indigo-600 font-medium">Ready for AI extraction</span>
141
+ </div>
142
+ </motion.div>
143
+ )}
144
+ </AnimatePresence>
145
+ </div>
146
+ );
147
+ }