diff --git a/Frontend/src/components/FileList.jsx b/Frontend/src/components/FileList.jsx index 22e4d1a..e7647a7 100644 --- a/Frontend/src/components/FileList.jsx +++ b/Frontend/src/components/FileList.jsx @@ -1,25 +1,83 @@ import React, { useState, useEffect } from "react"; import PropTypes from "prop-types"; +import { useDispatch } from "react-redux"; +import { setCurrentPath } from "../store/pathSlice"; +import { + FileText, + FileVideo, + FileImage, + FileAudio, + FileArchive, + FileSpreadsheet, + FileType2, + FileCode2, + Presentation, + Folder, + Download, + Trash2, + ArrowLeft, +} from "lucide-react"; const FileTable = ({ initialPath }) => { - const [currentPath, setCurrentPath] = useState(initialPath || "/"); + const [currentPath, setCurrentPathState] = useState(initialPath || "/"); const [files, setFiles] = useState([]); + const dispatch = useDispatch(); - // Helpers to parse entry const getType = (entry) => entry.trim().startsWith("📁") ? "Folder" : "File"; + const getName = (entry) => entry.trim().replace(/^📁\s*|^📄\s*/, ""); + const isFile = (entry) => getType(entry) === "File"; - // Fetch and show only top-level entries (indentation = 0) + const getIcon = (name, type) => { + if (type === "Folder") + return ; + const ext = name.split(".").pop().toLowerCase(); + switch (ext) { + case "txt": + return ; + case "mp4": + case "mkv": + return ; + case "jpg": + case "jpeg": + case "png": + case "gif": + return ; + case "mp3": + case "wav": + return ; + case "zip": + case "rar": + case "tar": + case "gz": + return ; + case "csv": + case "xls": + case "xlsx": + return ; + case "ppt": + case "pptx": + return ; + case "js": + case "html": + case "css": + case "java": + case "py": + case "cpp": + return ; + default: + return ; + } + }; + const fetchFiles = async () => { try { const response = await fetch( `http://192.168.29.61:8080/api/hdfs/listFiles?hdfsPath=${currentPath}` ); const data = await response.json(); - - // Filter entries: only those without leading spaces const filtered = data.filter( (entry) => entry.match(/^ */)[0].length === 0 ); @@ -30,53 +88,130 @@ const FileTable = ({ initialPath }) => { } }; + const deleteFileOrFolder = async (name, type, event) => { + event.stopPropagation(); // Prevent row onClick from firing. + try { + const hdfsPath = + currentPath === "/" ? `/${name}` : `${currentPath}/${name}`; + const encodedPath = encodeURIComponent(hdfsPath); + let deleteEndpoint = ""; + + if (type === "File") { + deleteEndpoint = `http://192.168.29.61:8080/api/hdfs/deleteFile?hdfsPath=${encodedPath}`; + } else { + deleteEndpoint = `http://192.168.29.61:8080/api/hdfs/deleteFolder?hdfsPath=${encodedPath}`; + } + + const response = await fetch(deleteEndpoint, { method: "DELETE" }); + + if (!response.ok) { + const errorText = await response.text(); + console.error("Deletion failed:", errorText); + } + + fetchFiles(); + } catch (error) { + console.error("Failed to delete file/folder:", error); + } + }; + useEffect(() => { + dispatch(setCurrentPath(currentPath)); fetchFiles(); }, [currentPath]); - // Navigate into a folder const handleOpenFolder = (folderName) => { const newPath = currentPath === "/" ? `/${folderName}` : `${currentPath}/${folderName}`; - setCurrentPath(newPath); + setCurrentPathState(newPath); }; - // Go up one level const goBack = () => { if (currentPath === "/") return; const parts = currentPath.split("/").filter(Boolean); parts.pop(); - setCurrentPath(parts.length === 0 ? "/" : `/${parts.join("/")}`); + setCurrentPathState(parts.length === 0 ? "/" : `/${parts.join("/")}`); + }; + + const handleFileDownload = async (hdfsPath, name, event) => { + event.stopPropagation(); // Prevent row click (if any) for files. + try { + const formData = new URLSearchParams(); + formData.append("hdfsPath", hdfsPath); + formData.append("username", "kalas"); + + const response = await fetch( + "http://192.168.29.61:8080/api/hdfs/downloadFile", + { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: formData.toString(), + } + ); + + if (!response.ok) throw new Error("Download failed"); + + // Extract filename from header OR fallback to name from path + const contentDisposition = response.headers.get("Content-Disposition"); + let fileName = "downloaded_file"; + + if (contentDisposition && contentDisposition.includes("filename=")) { + const match = contentDisposition.match( + /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/ + ); + if (match && match[1]) { + fileName = match[1].replace(/['"]/g, ""); + } + } else { + // fallback: extract name from path + const parts = hdfsPath.split("/"); + const fallback = parts[parts.length - 1]; + if (fallback) fileName = fallback; + } + + // Create blob and trigger download + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = fileName; + document.body.appendChild(link); + link.click(); + link.remove(); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error("Download failed:", error); + alert("Something went wrong while downloading the file."); + } }; return ( -
-
- Path: {currentPath} +
+
+ Path: {currentPath} {currentPath !== "/" && ( - )}
- + - - - + + {files.length === 0 ? ( - @@ -84,38 +219,40 @@ const FileTable = ({ initialPath }) => { files.map((entry, idx) => { const name = getName(entry); const type = getType(entry); - const encodedPath = encodeURIComponent(`${currentPath}/${name}`); - const downloadUrl = `http://192.168.29.61:8080/api/hdfs/downloadFile?hdfsPath=${encodedPath}&localPath=E:/testdownload/${name}&kalas=${ - currentPath.split("/")[1] || "user" - }`; + const hdfsPath = + currentPath === "/" ? `/${name}` : `${currentPath}/${name}`; return ( handleOpenFolder(name) : undefined + } + className={`even:bg-blue-50 odd:bg-white border-b border-blue-100 transition hover:bg-blue-100 ${ + type === "Folder" ? "cursor-pointer" : "" + }`} > - - - );
- File Name - - Type - - Action - NameActions
+ No files found.
+ + {getIcon(name, type)} {name} {type} - {isFile(entry) ? ( - - Download - - ) : ( + + {isFile(entry) && ( )} +