Noumankhan2005's picture
Initial commit: PRESAGE System with React frontend and FastAPI backend
19d6abb
'use client';
import React, { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { motion, AnimatePresence } from 'framer-motion';
import { CloudUploadIcon, FileText, CheckCircle2 } from 'lucide-react';
interface ImageDropzoneProps {
onFilesAccepted: (files: File[]) => void;
loading?: boolean;
}
export const ImageDropzone: React.FC<ImageDropzoneProps> = ({
onFilesAccepted,
loading = false,
}) => {
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
const onDrop = useCallback(
(acceptedFiles: File[]) => {
setUploadedFiles(acceptedFiles);
onFilesAccepted(acceptedFiles);
},
[onFilesAccepted]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'image/*': ['.jpeg', '.jpg', '.png', '.gif'],
'application/pdf': ['.pdf'],
},
disabled: loading,
});
return (
<div className="w-full">
<div
{...getRootProps()}
className={`group relative overflow-hidden rounded-3xl border-2 border-dashed transition-all duration-500 ${
isDragActive
? 'border-cyan-400 bg-cyan-500/10 scale-[1.02] shadow-[0_0_30px_rgba(6,182,212,0.2)]'
: 'border-slate-800 bg-slate-900/40 hover:border-cyan-500/30'
} ${loading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
>
<div className="absolute inset-0 bg-gradient-to-br from-cyan-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<input {...getInputProps()} />
<div className="relative z-10 flex flex-col items-center justify-center p-8 min-h-[240px]">
<motion.div
animate={isDragActive ? { y: -10, scale: 1.1 } : { y: 0, scale: 1 }}
className={`p-4 rounded-2xl ${isDragActive ? 'bg-cyan-500 text-white' : 'bg-slate-950 text-slate-500 group-hover:text-cyan-400'} mb-6 transition-colors`}
>
<CloudUploadIcon className="w-8 h-8" />
</motion.div>
<div className="text-center space-y-2">
<h4 className="text-lg font-bold text-white tracking-tight">
{isDragActive ? 'Release to upload' : 'Sync Medical Data'}
</h4>
<p className="text-xs font-mono text-slate-500 uppercase tracking-widest">
Drag images or <span className="text-cyan-500">browse file-system</span>
</p>
</div>
<AnimatePresence>
{uploadedFiles.length > 0 && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mt-8 flex flex-wrap justify-center gap-2"
>
{uploadedFiles.map((file, idx) => (
<div
key={idx}
className="flex items-center gap-2 px-3 py-1.5 bg-cyan-500/10 border border-cyan-500/20 rounded-full"
>
<FileText className="w-3 h-3 text-cyan-400" />
<span className="text-[10px] font-mono text-cyan-400">{file.name}</span>
<CheckCircle2 className="w-3 h-3 text-emerald-400" />
</div>
))}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
);
};
export default ImageDropzone;