Enhance file upload experience with drag-and-drop support and file removal functionality

This commit is contained in:
Chris-1010
2025-09-23 01:04:53 +01:00
parent f11bfb9d27
commit 76e609068f
2 changed files with 137 additions and 35 deletions

View File

@@ -29,7 +29,7 @@ body {
display: flex; display: flex;
place-items: center; place-items: center;
min-width: 320px; min-width: 320px;
min-height: 100vh; height: 98vh;
} }
h1 { h1 {

View File

@@ -1,39 +1,141 @@
import { useState } from 'react' import { useState, useRef } from "react";
import { useNavigate } from 'react-router-dom' import { useNavigate } from "react-router-dom";
import { readFiles } from '../model/parser' import { readFiles } from "../model/parser";
import { addStreams } from '../util/db' import { addStreams } from "../util/db";
const Home = () => { const Home = () => {
const [files, setFiles] = useState<File[]>([]) const [files, setFiles] = useState<File[]>([]);
const navigate = useNavigate() const [dragOver, setDragOver] = useState(false);
const navigate = useNavigate();
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFiles = (fileList: FileList | null) => {
if (fileList) {
const jsonFiles = Array.from(fileList).filter((file) => file.type === "application/json" || file.name.endsWith(".json"));
setFiles(jsonFiles);
}
};
//#region File handling methods
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
handleFiles(e.target.files);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
setDragOver(false);
handleFiles(e.dataTransfer.files);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
setDragOver(true);
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
setDragOver(false);
};
//#endregion
const handleUpload = async () => { const handleUpload = async () => {
if (files.length === 0) return; if (files.length === 0) return;
const streams = await readFiles(files); // your parser const streams = await readFiles(files);
await addStreams(streams); // save to IndexedDB await addStreams(streams);
navigate('/stats'); // go to StatView navigate("/stats");
};
const openFileDialog = () => {
fileInputRef.current?.click();
};
const removeFile = (index: number) => {
setFiles(files.filter((_, i) => i !== index));
}; };
return ( return (
<div> <div>
<h1>Upload Spotify files</h1> <h1>Upload Spotify files</h1>
<input
type="file" {/* Hidden actual input */}
accept=".json" <input ref={fileInputRef} type="file" accept=".json" multiple onChange={handleFileSelect} style={{ display: "none" }} />
multiple
onChange={(e) => { {/* Custom drop zone */}
if (e.target.files) { <div
setFiles(Array.from(e.target.files)); onClick={openFileDialog}
} onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
style={{
border: `2px dashed ${dragOver ? "#007bff" : "#ccc"}`,
borderRadius: "8px",
padding: "40px 20px",
textAlign: "center",
cursor: "pointer",
backgroundColor: dragOver ? "#f8f9fa1c" : "",
transition: "all 0.3s ease",
margin: "20px 0",
}} }}
/> >
<div style={{ fontSize: "48px", marginBottom: "10px" }}>📁</div>
<p style={{ margin: "10px 0", fontSize: "16px", fontWeight: "bold" }}>Drop JSON files here or click to browse</p>
<p style={{ margin: "5px 0", fontSize: "14px", color: "#666" }}>Multiple .json files accepted</p>
</div>
{files.length > 0 && (
<div style={{ margin: "20px 0" }}>
<h3>{files.length} files selected:</h3>
{files.map((file, index) => (
<div
key={index}
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "8px 12px",
margin: "5px 0",
borderRadius: "4px",
}}
>
<span>{file.name}</span>
<button <button
onClick={handleUpload}> onClick={(e) => {
Upload e.stopPropagation();
removeFile(index);
}}
style={{
background: "#ff4444",
color: "white",
border: "none",
borderRadius: "3px",
padding: "4px 8px",
cursor: "pointer",
}}
>
</button> </button>
</div> </div>
) ))}
} <button
onClick={handleUpload}
style={{
backgroundColor: "#007bff",
color: "white",
border: "none",
padding: "12px 24px",
borderRadius: "6px",
cursor: "pointer",
fontSize: "16px",
marginTop: "10px",
}}
>
Upload Files
</button>
</div>
)}
</div>
);
};
export default Home; export default Home;