| import { useState, useCallback } from "react"; |
| import { Upload, Video, X, Loader2 } from "lucide-react"; |
| import { Button } from "@/components/ui/button"; |
| import { cn } from "@/lib/utils"; |
|
|
| interface UploadZoneProps { |
| onUpload: (file: File) => void; |
| isLoading: boolean; |
| } |
|
|
| export const UploadZone = ({ onUpload, isLoading }: UploadZoneProps) => { |
| const [isDragging, setIsDragging] = useState(false); |
| const [selectedFile, setSelectedFile] = useState<File | null>(null); |
|
|
| const handleDrag = useCallback((e: React.DragEvent) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| }, []); |
|
|
| const handleDragIn = useCallback((e: React.DragEvent) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| setIsDragging(true); |
| }, []); |
|
|
| const handleDragOut = useCallback((e: React.DragEvent) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| setIsDragging(false); |
| }, []); |
|
|
| const handleDrop = useCallback((e: React.DragEvent) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| setIsDragging(false); |
|
|
| const files = e.dataTransfer.files; |
| if (files && files.length > 0) { |
| const file = files[0]; |
| if (file.type.startsWith("video/")) { |
| setSelectedFile(file); |
| } |
| } |
| }, []); |
|
|
| const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { |
| const files = e.target.files; |
| if (files && files.length > 0) { |
| setSelectedFile(files[0]); |
| } |
| }; |
|
|
| const handleSubmit = () => { |
| if (selectedFile) { |
| onUpload(selectedFile); |
| } |
| }; |
|
|
| const clearFile = () => { |
| setSelectedFile(null); |
| }; |
|
|
| return ( |
| <div className="mx-auto max-w-2xl"> |
| <div |
| className={cn( |
| "upload-zone relative flex flex-col items-center justify-center p-12 text-center", |
| isDragging && "dragging", |
| selectedFile && "border-primary/50 bg-accent/30" |
| )} |
| onDragEnter={handleDragIn} |
| onDragLeave={handleDragOut} |
| onDragOver={handleDrag} |
| onDrop={handleDrop} |
| > |
| {!selectedFile ? ( |
| <> |
| <div className="mb-6 flex h-20 w-20 items-center justify-center rounded-2xl bg-primary/10"> |
| <Upload className="h-10 w-10 text-primary animate-float" /> |
| </div> |
| <h3 className="mb-2 font-display text-xl font-semibold"> |
| Upload Echocardiography Video |
| </h3> |
| <p className="mb-6 text-muted-foreground"> |
| Drag and drop your video file here, or click to browse |
| </p> |
| <input |
| type="file" |
| accept="video/*" |
| onChange={handleFileSelect} |
| className="hidden" |
| id="file-upload" |
| /> |
| <label htmlFor="file-upload"> |
| <Button variant="gradient" className="cursor-pointer" asChild> |
| <span>Select Video File</span> |
| </Button> |
| </label> |
| <p className="mt-4 text-xs text-muted-foreground"> |
| Supported formats: MP4, AVI, MOV, DICOM |
| </p> |
| </> |
| ) : ( |
| <div className="w-full"> |
| <div className="mb-6 flex items-center justify-center gap-4"> |
| <div className="flex h-16 w-16 items-center justify-center rounded-xl bg-primary/10"> |
| <Video className="h-8 w-8 text-primary" /> |
| </div> |
| <div className="flex-1 text-left"> |
| <p className="font-medium truncate max-w-xs"> |
| {selectedFile.name} |
| </p> |
| <p className="text-sm text-muted-foreground"> |
| {(selectedFile.size / (1024 * 1024)).toFixed(2)} MB |
| </p> |
| </div> |
| <Button |
| variant="ghost" |
| size="icon" |
| onClick={clearFile} |
| disabled={isLoading} |
| > |
| <X className="h-5 w-5" /> |
| </Button> |
| </div> |
| <Button |
| variant="gradient" |
| size="lg" |
| onClick={handleSubmit} |
| disabled={isLoading} |
| className="w-full" |
| > |
| {isLoading ? ( |
| <> |
| <Loader2 className="h-5 w-5 animate-spin" /> |
| Analyzing... |
| </> |
| ) : ( |
| <> |
| <Upload className="h-5 w-5" /> |
| Start Analysis |
| </> |
| )} |
| </Button> |
| </div> |
| )} |
| </div> |
| </div> |
| ); |
| }; |
|
|