initial commit
This commit is contained in:
113
frontend/src/features/uploader/components/FileUploader.tsx
Normal file
113
frontend/src/features/uploader/components/FileUploader.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { useGridStore } from "@/store/use-grid-store";
|
||||
import { parseArrowStream } from "@/lib/arrow-client";
|
||||
import { Upload, FileSpreadsheet, Loader2, AlertCircle } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { getApiUrl } from "@/lib/api-config";
|
||||
|
||||
export function FileUploader() {
|
||||
const { setLoading, setData, setError, isLoading } = useGridStore();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const handleFile = async (file: File) => {
|
||||
console.log("=== UPLOAD START ===", file.name, file.size);
|
||||
setLoading(true);
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
try {
|
||||
console.log("Fetching:", getApiUrl("/upload"));
|
||||
const response = await fetch(getApiUrl("/upload"), {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
console.log("Response status:", response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) throw new Error("Backend non trouvé (404). Vérifiez l'URL.");
|
||||
if (response.status === 500) throw new Error("Erreur interne du serveur.");
|
||||
throw new Error(await response.text());
|
||||
}
|
||||
|
||||
const metadataStr = response.headers.get("X-Column-Metadata");
|
||||
console.log("Metadata:", metadataStr);
|
||||
const metadata = metadataStr ? JSON.parse(metadataStr) : [];
|
||||
console.log("Parsing Arrow...");
|
||||
const data = await parseArrowStream(response);
|
||||
console.log("Data parsed, rows:", data.length);
|
||||
|
||||
setData(data, metadata);
|
||||
console.log("=== UPLOAD SUCCESS ===");
|
||||
} catch (err: any) {
|
||||
console.error("Upload Error:", err);
|
||||
// Check for network error (Failed to fetch)
|
||||
if (err.message === "Failed to fetch") {
|
||||
setError("Impossible de contacter le serveur. Le backend est-il lancé sur le port 8000 ?");
|
||||
} else {
|
||||
setError(err.message || "Échec de l'upload");
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const onDragLeave = () => setIsDragging(false);
|
||||
|
||||
const onDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
const file = e.dataTransfer.files?.[0];
|
||||
if (file) handleFile(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative group flex items-center gap-6 p-6 rounded-2xl border-2 border-dashed transition-all duration-300 cursor-pointer overflow-hidden",
|
||||
isDragging
|
||||
? "border-indigo-500 bg-indigo-50/50 scale-[1.02] shadow-xl shadow-indigo-100"
|
||||
: "border-slate-200 bg-white hover:border-indigo-300 hover:shadow-md"
|
||||
)}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"
|
||||
accept=".xlsx,.xls,.csv"
|
||||
onChange={(e) => e.target.files?.[0] && handleFile(e.target.files[0])}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
|
||||
<div className={cn(
|
||||
"p-4 rounded-xl transition-colors",
|
||||
isDragging ? "bg-indigo-100 text-indigo-600" : "bg-slate-100 text-slate-500 group-hover:bg-indigo-50 group-hover:text-indigo-600"
|
||||
)}>
|
||||
{isLoading ? <Loader2 className="w-8 h-8 animate-spin" /> : <Upload className="w-8 h-8" />}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-1">
|
||||
<h3 className="text-lg font-bold text-slate-900 group-hover:text-indigo-700 transition-colors">
|
||||
{isLoading ? "Traitement en cours..." : "Déposez votre fichier ici"}
|
||||
</h3>
|
||||
<p className="text-sm text-slate-500">
|
||||
Excel (.xlsx) ou CSV. <span className="text-xs bg-slate-100 px-2 py-0.5 rounded text-slate-400">Max 50MB</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="hidden sm:flex items-center gap-2 text-xs font-medium text-slate-400 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-100">
|
||||
<FileSpreadsheet className="w-4 h-4" />
|
||||
<span>Auto-Detect</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user