3d_model / web-ui /app /components /FileUpload.tsx
Azan
Fix API URLs: Use relative paths for deployment
0780458
import { useState, useRef } from 'react';
interface FileUploadProps {
onUploadComplete: (path: string) => void;
}
export default function FileUpload({ onUploadComplete }: FileUploadProps) {
const [isUploading, setIsUploading] = useState(false);
const [dragActive, setDragActive] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const handleFiles = async (files: FileList | null) => {
if (!files || files.length === 0) return;
const file = files[0];
setIsUploading(true);
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/v1/dataset/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const text = await response.text();
let detail = 'Upload failed';
try {
const json = JSON.parse(text);
detail = json.detail || detail;
} catch {
detail = `Server error (${response.status}): ${text.slice(0, 100)}`;
}
throw new Error(detail);
}
const data = await response.json();
const uploadPath = `data/uploaded_datasets/${file.name.replace('.zip', '')}`;
onUploadComplete(uploadPath);
} catch (error: any) {
console.error(error);
alert(`Error: ${error.message}`);
} finally {
setIsUploading(false);
}
};
return (
<div
className={`relative border-2 border-dashed rounded-xl p-8 text-center transition-all ${dragActive ? 'border-indigo-500 bg-indigo-500/10' : 'border-gray-700 hover:border-indigo-400/50'
}`}
onDragEnter={(e) => { e.preventDefault(); e.stopPropagation(); setDragActive(true); }}
onDragLeave={(e) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); }}
onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); }}
onDrop={(e) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
handleFiles(e.dataTransfer.files);
}}
>
<input
ref={inputRef}
type="file"
className="hidden"
accept=".zip,.mp4,.mov"
onChange={(e) => handleFiles(e.target.files)}
/>
{isUploading ? (
<div className="flex flex-col items-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-500 mb-2"></div>
<p className="text-gray-400">Uploading...</p>
</div>
) : (
<div onClick={() => inputRef.current?.click()} className="cursor-pointer">
<svg className="w-10 h-10 text-gray-500 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p className="text-gray-300 font-medium">Click to upload ARKit Video / Zip</p>
<p className="text-gray-500 text-sm mt-1">or drag and drop here</p>
</div>
)}
</div>
);
}