"use client"; import { FormEvent, useEffect, useRef, useState, DragEvent } from "react"; import { useRouter } from "next/navigation"; type FileRow = { id: string; name: string; relativePath: string; contentType: string; sizeBytes: number; createdAt: string | Date; }; const MAX_UPLOAD_SIZE = 10 * 1024 * 1024 * 1024; // 10GB in bytes function formatBytes(bytes: number) { if (!bytes) return "0 B"; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), sizes.length - 1); const value = bytes / Math.pow(1024, i); return `${value.toFixed(value >= 10 || value % 1 === 0 ? 0 : 1)} ${sizes[i]}`; } function getFileIcon(contentType: string) { if (contentType.startsWith("image/")) { return ( ); } if (contentType.startsWith("video/")) { return ( ); } if (contentType.includes("pdf")) { return ( ); } return ( ); } export default function DashboardPage() { const router = useRouter(); const [files, setFiles] = useState([]); const [loadingList, setLoadingList] = useState(true); const [uploading, setUploading] = useState(false); const [error, setError] = useState(null); const [isDragging, setIsDragging] = useState(false); const [deletingAll, setDeletingAll] = useState(false); const [showDeleteAllConfirm, setShowDeleteAllConfirm] = useState(false); const [deletingFileId, setDeletingFileId] = useState(null); const [selectedFiles, setSelectedFiles] = useState([]); const fileInputRef = useRef(null); const folderInputRef = useRef(null); const loadFiles = async () => { setLoadingList(true); const res = await fetch("/api/files/list"); const data = await res.json(); setLoadingList(false); if (!res.ok) { setError(data?.error ?? "Unable to load files"); return; } setFiles(data.files ?? []); }; useEffect(() => { loadFiles(); }, []); const handleFileSelect = (e: React.ChangeEvent) => { const files = e.target.files; if (files && files.length > 0) { const fileArray = Array.from(files); setSelectedFiles((prev) => [...prev, ...fileArray]); } // Reset input to allow selecting the same file again e.target.value = ""; }; const getTotalSelectedSize = () => { return selectedFiles.reduce((acc, f) => acc + f.size, 0); }; const isOverSizeLimit = () => { return getTotalSelectedSize() > MAX_UPLOAD_SIZE; }; const handleUpload = async (e: FormEvent) => { e.preventDefault(); if (selectedFiles.length === 0) { setError("Please choose at least one file or folder."); return; } if (isOverSizeLimit()) { setError("Total upload size exceeds 10GB limit. Please remove some files."); return; } const formData = new FormData(); selectedFiles.forEach((file) => { const fileWithPath = file as File & { webkitRelativePath?: string }; const filename = fileWithPath.webkitRelativePath || fileWithPath.name; formData.append("files", fileWithPath, filename); }); setUploading(true); setError(null); const res = await fetch("/api/files/upload", { method: "POST", body: formData, }); const data = await res.json(); setUploading(false); if (!res.ok) { setError(data?.error ?? "Upload failed"); return; } setFiles((prev) => [...data.files, ...prev]); setSelectedFiles([]); }; const clearSelectedFiles = () => { setSelectedFiles([]); }; const addFilesToSelection = (fileList: FileList) => { setSelectedFiles((prev) => [...prev, ...Array.from(fileList)]); }; const handleDragOver = (e: DragEvent) => { e.preventDefault(); setIsDragging(true); }; const handleDragLeave = (e: DragEvent) => { e.preventDefault(); setIsDragging(false); }; const handleDrop = (e: DragEvent) => { e.preventDefault(); setIsDragging(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { addFilesToSelection(e.dataTransfer.files); } }; const handleDownload = async (id: string) => { const res = await fetch(`/api/files/${id}/download`); const data = await res.json(); if (!res.ok) { setError(data?.error ?? "Unable to download"); return; } window.location.assign(data.url); }; const handleDelete = async (id: string) => { setDeletingFileId(id); setError(null); const res = await fetch(`/api/files/${id}/delete`, { method: "DELETE" }); const data = await res.json(); setDeletingFileId(null); if (!res.ok) { setError(data?.error ?? "Unable to delete"); return; } setFiles((prev) => prev.filter((f) => f.id !== id)); }; const handleDeleteAll = async () => { setDeletingAll(true); setError(null); const res = await fetch("/api/files/delete-all", { method: "DELETE" }); const data = await res.json(); setDeletingAll(false); setShowDeleteAllConfirm(false); if (!res.ok) { setError(data?.error ?? "Unable to delete all files"); return; } setFiles([]); }; const handleLogout = async () => { await fetch("/api/auth/logout", { method: "POST" }); router.push("/login"); }; return (
{/* Header */}
{/* Logo */}

Asa's FTP

Asa's FTP Storage

{/* Main Content */}
{/* Upload Section */}

Upload Files

{/* Drop Zone */}

{isDragging ? "Drop files here" : "Drag & drop files here"}

or select files/folders from your computer

{/* Hidden file inputs */}
{/* Selected Files Preview */} {selectedFiles.length > 0 && (

{selectedFiles.length} {selectedFiles.length === 1 ? "file" : "files"} selected ({formatBytes(getTotalSelectedSize())} / 10 GB max)

{isOverSizeLimit() && (
Total size exceeds 10GB limit. Remove some files to continue.
)}
{selectedFiles.slice(0, 10).map((file, idx) => ( {file.name.length > 25 ? file.name.slice(0, 22) + "..." : file.name} ))} {selectedFiles.length > 10 && ( +{selectedFiles.length - 10} more )}
)} {/* Upload Button */}

{selectedFiles.length === 0 ? "Supports files and entire folders with preserved structure" : "Ready to upload"}

{/* Error Message */} {error && (

{error}

)}
{/* Files Section */}

Your Files

{files.length} {files.length === 1 ? "FILE" : "FILES"}
{files.length > 0 && ( )}
{loadingList ? (
Loading your files...
) : files.length === 0 ? (

No files yet

Upload your first file or folder using the upload section above.

) : ( <> {/* Desktop Table */}
{files.map((file, index) => ( ))}
Name Path Size Actions
{getFileIcon(file.contentType)} {file.name} {file.relativePath} {formatBytes(file.sizeBytes)}
{/* Mobile Cards */}
{files.map((file, index) => (
{getFileIcon(file.contentType)}

{file.name}

{file.relativePath}

{formatBytes(file.sizeBytes)}

))}
)}
{/* Delete All Confirmation Modal */} {showDeleteAllConfirm && (

Delete All Files?

This action cannot be undone

You are about to permanently delete {files.length} {files.length === 1 ? "file" : "files"} from your storage. This action is irreversible.

)} {/* Footer */}
); }