From 1f40b02346c24e7e5a4ff757ad3266beb22b4523 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:15:05 +0530 Subject: [PATCH 01/27] Feat:Added uploadFile.jsx for in UserPages --- Frontend/src/pages/UserPages/UploadFile.jsx | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Frontend/src/pages/UserPages/UploadFile.jsx diff --git a/Frontend/src/pages/UserPages/UploadFile.jsx b/Frontend/src/pages/UserPages/UploadFile.jsx new file mode 100644 index 0000000..81bfea9 --- /dev/null +++ b/Frontend/src/pages/UserPages/UploadFile.jsx @@ -0,0 +1,57 @@ +import React, { useState } from "react"; +// Adjust the import for FileUpload to the correct location: +import FileUploadModal from "../../components/FileUploadModal"; +const UploadFile = () => { + // Control modal visibility with state + const [isModalOpen, setIsModalOpen] = useState(false); + + // Dummy callback or proper function if necessary + const fetchFiles = () => { + console.log("Upload succeeded, fetch or update file list here."); + }; + + return ( + <> + {/* Button or trigger to open the modal */} + + +
+
+
+
+

+ Upload File +

+ +
+
+ +
+
+
+
+ + ); +}; + +export default UploadFile; From 00f6e28207215b186fc842da1d1acca0bbb98daf Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:19:55 +0530 Subject: [PATCH 02/27] Feat:Added file icons and fixed download errors --- Frontend/src/components/FileList.jsx | 229 +++++++++++++++++++++------ 1 file changed, 183 insertions(+), 46 deletions(-) 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" : "" + }`} > - - - ); From 4ab49db6afe2a92dad2da37198cc6f0686ecb7a5 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:22:12 +0530 Subject: [PATCH 03/27] Feat:Added redux for the hdfspath to change the uplaod the file onto folder --- Frontend/src/store/store.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Frontend/src/store/store.js diff --git a/Frontend/src/store/store.js b/Frontend/src/store/store.js new file mode 100644 index 0000000..0ded312 --- /dev/null +++ b/Frontend/src/store/store.js @@ -0,0 +1,9 @@ +// src/redux/store.js +import { configureStore } from "@reduxjs/toolkit"; +import pathReducer from "./pathSlice"; + +export const store = configureStore({ + reducer: { + path: pathReducer, + }, +}); From df4d3c1990572472aa71bf661f3379649cda7d93 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:25:04 +0530 Subject: [PATCH 04/27] Fix:deleted api.js and FileUpload.jsx and made new files as needed --- Frontend/src/components/FileUpload.jsx | 68 -------------------------- Frontend/src/store/api.js | 0 2 files changed, 68 deletions(-) delete mode 100644 Frontend/src/components/FileUpload.jsx delete mode 100644 Frontend/src/store/api.js diff --git a/Frontend/src/components/FileUpload.jsx b/Frontend/src/components/FileUpload.jsx deleted file mode 100644 index 2d017c8..0000000 --- a/Frontend/src/components/FileUpload.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useState } from "react"; -import { uploadFileToHDFS } from "../utils/api"; - -const FileUpload = () => { - const [file, setFile] = useState(null); - const [hdfsPath, setHdfsPath] = useState("/kalas"); - const [uploadedFileName, setUploadedFileName] = useState(""); - const [username, setUsername] = useState("kalas"); - - const handleSubmit = (e) => { - e.preventDefault(); - if (!file || !uploadedFileName) { - return; - } - uploadFileToHDFS(file, hdfsPath, uploadedFileName, username); - }; - - return ( - -

Upload File Your File

- - { - setFile(e.target.files[0]); - setUploadedFileName(e.target.files[0]?.name || ""); - }} - className="mb-2" - /> - - {/* setHdfsPath(e.target.value)} - className="border p-2 mb-2 w-full" - /> */} - - {/* setUploadedFileName(e.target.value)} - className="border p-2 mb-2 w-full" - /> */} - - {/* setUsername(e.target.value)} - className="border p-2 mb-2 w-full" - /> */} - - - - ); -}; - -export default FileUpload; diff --git a/Frontend/src/store/api.js b/Frontend/src/store/api.js deleted file mode 100644 index e69de29..0000000 From 6aae767aa60ae4808a1763c14d6ff1c6cc2788cf Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:25:40 +0530 Subject: [PATCH 05/27] Fix:Changed title of the project --- Frontend/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/index.html b/Frontend/index.html index b6120e1..f4f4d16 100644 --- a/Frontend/index.html +++ b/Frontend/index.html @@ -8,7 +8,7 @@ - Drive-thru + Skycrate From e59784bfa9d7196de91425302047c91351c58f17 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:28:20 +0530 Subject: [PATCH 06/27] Feat:Added slice for redux for hdfspath --- Frontend/src/store/pathSlice.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Frontend/src/store/pathSlice.js diff --git a/Frontend/src/store/pathSlice.js b/Frontend/src/store/pathSlice.js new file mode 100644 index 0000000..9ece651 --- /dev/null +++ b/Frontend/src/store/pathSlice.js @@ -0,0 +1,19 @@ +// src/redux/pathSlice.js +import { createSlice } from "@reduxjs/toolkit"; + +const initialState = { + currentPath: "/", +}; + +const pathSlice = createSlice({ + name: "path", + initialState, + reducers: { + setCurrentPath: (state, action) => { + state.currentPath = action.payload; + }, + }, +}); + +export const { setCurrentPath } = pathSlice.actions; +export default pathSlice.reducer; From 9632450d1607ac9e9e7435a87f83a360cd183bd2 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:36:49 +0530 Subject: [PATCH 07/27] Feat:Added logic to upload the file to the Skycrate --- Frontend/src/components/FileUploadModal.jsx | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Frontend/src/components/FileUploadModal.jsx diff --git a/Frontend/src/components/FileUploadModal.jsx b/Frontend/src/components/FileUploadModal.jsx new file mode 100644 index 0000000..b47341c --- /dev/null +++ b/Frontend/src/components/FileUploadModal.jsx @@ -0,0 +1,87 @@ +import React, { useState } from "react"; +import { useSelector } from "react-redux"; + +const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { + const currentPath = useSelector((state) => state.path.currentPath); + const [file, setFile] = useState(null); + const [message, setMessage] = useState(""); + const [uploading, setUploading] = useState(false); + + const uploadFileToHDFS = async () => { + if (!file) { + setMessage("Please select a file before uploading."); + return; + } + + const formData = new FormData(); + formData.append("file", file); + formData.append("hdfsPath", currentPath); + formData.append("uploadedFileName", file.name); + formData.append("username", "kalas"); + + try { + setUploading(true); + setMessage("Uploading file..."); + + const response = await fetch( + `http://192.168.29.61:8080/api/hdfs/uploadFile`, + { method: "POST", body: formData } + ); + + if (!response.ok) { + const errorText = await response.text(); + setMessage(`Upload failed: ${errorText}`); + } else { + setMessage("File uploaded successfully!"); + onUploadSuccess(); // Notify Dashboard to refresh file list + setTimeout(onClose, 1000); + } + } catch (error) { + console.error(error); + setMessage("An error occurred during upload."); + } finally { + setUploading(false); + } + }; + + if (!show) return null; + + return ( +
+
+
+
+

Upload File

+ +
+
+ setFile(e.target.files[0])} + className="w-full mb-4" + /> + {message &&

{message}

} +
+ + +
+
+
+
+ ); +}; + +export default FileUploadModal; From f6371faf9a7cbd68809a458d9f2fbbe270f0ef55 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:40:14 +0530 Subject: [PATCH 08/27] Fix:Changed name of the project --- Frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/package.json b/Frontend/package.json index ee9f2ab..7f2bdff 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -1,5 +1,5 @@ { - "name": "drive-thru", + "name": "Skycrate", "private": true, "version": "0.0.0", "type": "module", From 9d6387699ec0224288c2acb5007ac4758f45fd1f Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:41:07 +0530 Subject: [PATCH 09/27] Feat:Added provider for the redux --- Frontend/src/main.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Frontend/src/main.jsx b/Frontend/src/main.jsx index 73fc17b..2439a22 100644 --- a/Frontend/src/main.jsx +++ b/Frontend/src/main.jsx @@ -2,9 +2,15 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; import App from "./App.jsx"; +import { Provider } from "react-redux"; +import { store } from "./store/store"; -createRoot(document.getElementById("root")).render( +const container = document.getElementById("root"); +const root = createRoot(container); +root.render( - + + + ); From 65ca53b22459d247eeb2e4f73aefbb0dbc4c95c0 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:43:02 +0530 Subject: [PATCH 10/27] Fix:Changed project name --- Frontend/src/components/Footer.jsx | 46 +++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/Frontend/src/components/Footer.jsx b/Frontend/src/components/Footer.jsx index 1150cc0..528c0c5 100644 --- a/Frontend/src/components/Footer.jsx +++ b/Frontend/src/components/Footer.jsx @@ -42,22 +42,38 @@ const Footer = () => { />
-

Drive-thru

+

Skycrate

Your secure cloud storage solution for all your digital needs.

@@ -117,7 +133,8 @@ const Footer = () => {

Stay Updated

- Get exclusive tips, updates on new features, and special offers directly in your inbox. + Get exclusive tips, updates on new features, and special offers + directly in your inbox.

{ {/* Bottom Section */}
-

© {new Date().getFullYear()} Drive-Thru. All rights reserved.

+

© {new Date().getFullYear()} Skycrate. All rights reserved.

From a91d7fe8c7acce0e21d781ea0b5edf866f756860 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:45:50 +0530 Subject: [PATCH 11/27] Fix: Removed extra landingpage on the src/pages path --- Frontend/src/components/Sidebar.jsx | 8 +- Frontend/src/pages/DrivethruLandingPage.jsx | 287 ------------------ .../pages/UserPages/DrivethruLandingPage.jsx | 4 +- 3 files changed, 6 insertions(+), 293 deletions(-) delete mode 100644 Frontend/src/pages/DrivethruLandingPage.jsx diff --git a/Frontend/src/components/Sidebar.jsx b/Frontend/src/components/Sidebar.jsx index beea1a0..a667fa9 100644 --- a/Frontend/src/components/Sidebar.jsx +++ b/Frontend/src/components/Sidebar.jsx @@ -33,10 +33,10 @@ const Sidebar = () => { Drive-thru Logo - - Drive-thru + + Skycrate
@@ -87,7 +87,7 @@ const Sidebar = () => { className="text-lg font-medium text-white truncate dark:text-gray-300" role="none" > - Drive-thru@Drive-thru.com + Skycrate@Skycrate.com

    diff --git a/Frontend/src/pages/DrivethruLandingPage.jsx b/Frontend/src/pages/DrivethruLandingPage.jsx deleted file mode 100644 index 93b0c37..0000000 --- a/Frontend/src/pages/DrivethruLandingPage.jsx +++ /dev/null @@ -1,287 +0,0 @@ -import React from "react"; -import Footer from "../components/Footer"; -{ - /* */ -} - -const DrivethruLandingPage = () => { - return ( -
    -
    -
    -
    -
    - -
    -
    -
    - - - -
    -

    Drive-thru

    -
    - -

    - Store, Access & Share Your Files — Anytime, Anywhere! -

    - -

    - A simple, secure, and fast cloud storage solution for all your - files. Upload, organize, and access with ease. -

    - -
    - - -
    -
    -
    -
    -
    - -
    -
    -
    - {" "} -
    - Person using Drive-thru on laptop -
    - {/* Features Card */} -
    -
    -

    Key Features

    - -
    -
    -
    - - - -
    -

    - "Easy Upload & Access" – Drag & drop, instant access. -

    -
    - -
    -
    - - - -
    -

    - "Secure & Private" – End-to-end encryption. -

    -
    - -
    -
    - - - -
    -

    - "Seamless Sharing" – Share files with one click. -

    -
    - -
    -
    - - - -
    -

    - "Access Anywhere" – Works on all devices. -

    -
    -
    -
    -
    -
    - - {/* How It Works Section */} -
    - {/* Person with Phone Image */} -
    - Person using Drive-thru on phone -
    - - {/* How It Works Card */} -
    -
    -

    How It Works

    - -
    -
    -
    - - - -
    -

    - Create an account – Sign up in seconds. -

    -
    - -
    -
    - - - -
    -

    - Upload files – Drag & drop or select from your device. -

    -
    - -
    -
    - - - - -
    -

    - Manage files – Rename, move, or delete easily. -

    -
    - -
    -
    - - - -
    -

    - Access anytime – Open files from any device. -

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - ); -}; - -export default DrivethruLandingPage; diff --git a/Frontend/src/pages/UserPages/DrivethruLandingPage.jsx b/Frontend/src/pages/UserPages/DrivethruLandingPage.jsx index 85f8534..97c3395 100644 --- a/Frontend/src/pages/UserPages/DrivethruLandingPage.jsx +++ b/Frontend/src/pages/UserPages/DrivethruLandingPage.jsx @@ -234,7 +234,7 @@ const DrivethruLandingPage = () => {

- Drive-thru + Skycrate

@@ -268,7 +268,7 @@ const DrivethruLandingPage = () => {
Drive-thru Dashboard Interface
From 5391410609b05e402e4641a8b84d41f8ae4f5108 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:47:11 +0530 Subject: [PATCH 12/27] Fix:Fixed api call for donwloading files --- Frontend/src/pages/UserPages/Dashboard.jsx | 109 ++++++--------------- 1 file changed, 31 insertions(+), 78 deletions(-) diff --git a/Frontend/src/pages/UserPages/Dashboard.jsx b/Frontend/src/pages/UserPages/Dashboard.jsx index 9e1a582..6845f59 100644 --- a/Frontend/src/pages/UserPages/Dashboard.jsx +++ b/Frontend/src/pages/UserPages/Dashboard.jsx @@ -1,109 +1,62 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import Sidebar from "../../components/Sidebar"; import FileList from "../../components/FileList"; -import FileUpload from "../../components/FileUpload"; +import FileUploadModal from "../../components/FileUploadModal"; // renamed the import accordingly + import { FiPlus } from "react-icons/fi"; const Dashboard = () => { - const [files, setFiles] = React.useState([]); + const [files, setFiles] = useState([]); + // State to manage upload modal visibility + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); - React.useEffect(() => { - const fetchData = async () => { + const fetchFiles = async () => { + try { const response = await fetch( "http://192.168.29.61:8080/api/hdfs/listFiles?hdfsPath=/" ); const data = await response.json(); setFiles(data); - }; - fetchData(); + } catch (error) { + console.error("Failed to fetch files:", error); + } + }; + + useEffect(() => { + fetchFiles(); + // downloadFile("/sonali/cc.pptx", "kalas"); }, []); return ( <> - {/* */} -

Dashboard

+ {/* Use onClick to toggle the modal */}
- - -
- {/* */} -
+
+ + {/* Render the FileUploadModal with proper props */} + setIsUploadModalOpen(false)} + onUploadSuccess={() => { + fetchFiles(); + // Optionally close the modal after upload success + setIsUploadModalOpen(false); + }} + /> ); }; From 76f9b0062431a3df09b042b59dde891bdf1eb9b4 Mon Sep 17 00:00:00 2001 From: vedang29 Date: Fri, 18 Apr 2025 16:33:40 +0530 Subject: [PATCH 13/27] Implemented sign in & sign up (with token storage) --- Frontend/.env | 1 + Frontend/package-lock.json | 34 +- Frontend/package.json | 1 + Frontend/src/components/Sidebar.jsx | 368 ++++++++----------- Frontend/src/pages/Authentication/Login.jsx | 150 ++++++-- Frontend/src/pages/Authentication/SignUp.jsx | 193 +++++++++- 6 files changed, 487 insertions(+), 260 deletions(-) create mode 100644 Frontend/.env diff --git a/Frontend/.env b/Frontend/.env new file mode 100644 index 0000000..eea892b --- /dev/null +++ b/Frontend/.env @@ -0,0 +1 @@ +VITE_API_URL=http://192.168.29.61:8080 diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index fa6a50a..f247e76 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "drive-thru", + "name": "Skycrate", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "drive-thru", + "name": "Skycrate", "version": "0.0.0", "dependencies": { "@reduxjs/toolkit": "^2.6.0", @@ -13,6 +13,7 @@ "lucide-react": "^0.476.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hot-toast": "^2.5.2", "react-icons": "^5.5.0", "react-redux": "^9.2.0", "react-router-dom": "^7.2.0" @@ -1929,8 +1930,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-view-buffer": { "version": "1.0.2", @@ -2767,6 +2767,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -4036,6 +4045,23 @@ "react": "^19.0.0" } }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-icons": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index 7f2bdff..1cb21f3 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -15,6 +15,7 @@ "lucide-react": "^0.476.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hot-toast": "^2.5.2", "react-icons": "^5.5.0", "react-redux": "^9.2.0", "react-router-dom": "^7.2.0" diff --git a/Frontend/src/components/Sidebar.jsx b/Frontend/src/components/Sidebar.jsx index a667fa9..1b252af 100644 --- a/Frontend/src/components/Sidebar.jsx +++ b/Frontend/src/components/Sidebar.jsx @@ -1,144 +1,171 @@ import { Link } from "react-router-dom"; - +import { toast } from 'react-hot-toast'; +import { useNavigate } from 'react-router-dom'; const Sidebar = () => { + + const navigate = useNavigate(); // Hook for programmatic navigation + + const handleLogout = () => { + // Show loading toast + const loadingToast = toast.loading("Logging out..."); + + // Simulate a delay (for example, network request) + setTimeout(() => { + // Remove the token from localStorage + localStorage.removeItem('token'); // Adjust the key if necessary + + // Redirect user to the homepage + navigate('/'); + + // Show success toast after logout + toast.update(loadingToast, { + render: "Logged out successfully!", + type: "success", + isLoading: false, + autoClose: 2000, + }); + }, 1500); // Simulate a 1.5 second delay before showing success + }; + return ( <> - - +
+ ); }; -export default Sidebar; +export default Sidebar; \ No newline at end of file diff --git a/Frontend/src/pages/Authentication/Login.jsx b/Frontend/src/pages/Authentication/Login.jsx index 919bf72..8f755b6 100644 --- a/Frontend/src/pages/Authentication/Login.jsx +++ b/Frontend/src/pages/Authentication/Login.jsx @@ -1,14 +1,76 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import { FiEye, FiEyeOff } from "react-icons/fi"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; +import toast from "react-hot-toast"; // Import React Hot Toast + + +const API_URL = import.meta.env.VITE_API_URL; // Using .env variable const Login = () => { - const [showPassword, setShowPassword] = React.useState(false); + const [showPassword, setShowPassword] = useState(false); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); // For navigation + + useEffect(() => { + // Check if token is present in localStorage and redirect to Dashboard + if (localStorage.getItem("token")) { + navigate("/dashboard"); // Redirect to Dashboard + } + }, [navigate]); const togglePassword = () => { setShowPassword(!showPassword); }; + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + + // Show loading toast + const toastId = toast.loading("Logging in..."); + + try { + const response = await fetch(`${API_URL}/api/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email, + password, + }), + }); + + const data = await response.json(); + + // Dismiss the loading toast after the response + toast.dismiss(toastId); + + if (response.ok) { + // On success, store the token in localStorage + localStorage.setItem("token", data.token); + localStorage.setItem("expiresIn", data.expiresIn); + + // Show success toast + toast.success("Login successful!"); + + // Redirect to Dashboard + navigate("/dashboard"); + } else { + // Show error toast if login fails + toast.error(data.message || "Login failed."); + } + } catch (error) { + // Dismiss the loading toast and show error + toast.dismiss(toastId); + toast.error("An error occurred. Please try again."); + } finally { + setLoading(false); + } + }; + return (
@@ -16,44 +78,56 @@ const Login = () => { Log in -
-
- +
+
+
+ setEmail(e.target.value)} + required + /> +
-
-
-
- - +
+
+
+ - {showPassword ? : } - + Forgot password? +
-
-
- - Forgot password? - -
- + {loading ? "Logging In..." : "Login"} + +

Don’t have an account?{" "} @@ -70,4 +144,4 @@ const Login = () => { ); }; -export default Login; +export default Login; \ No newline at end of file diff --git a/Frontend/src/pages/Authentication/SignUp.jsx b/Frontend/src/pages/Authentication/SignUp.jsx index 2ebe0e7..ab265ce 100644 --- a/Frontend/src/pages/Authentication/SignUp.jsx +++ b/Frontend/src/pages/Authentication/SignUp.jsx @@ -1,41 +1,115 @@ -// eslint-disable-next-line no-unused-vars import React, { useState } from "react"; import { FiEye, FiEyeOff } from "react-icons/fi"; import { Link } from "react-router-dom"; +import toast, { Toaster } from "react-hot-toast"; + +// const API_BASE_URL = process.env.REACT_APP_API_URL; +const API_BASE_URL = import.meta.env.VITE_API_URL; + const SignUp = () => { + const [formData, setFormData] = useState({ + firstname: "", + lastname: "", + email: "", + password: "", + confirmPassword: "", + }); + const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [loading, setLoading] = useState(false); + + const handleChange = (e) => { + setFormData((prev) => ({ + ...prev, + [e.target.name]: e.target.value, + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (formData.password !== formData.confirmPassword) { + toast.error("Passwords do not match."); + return; + } + + try { + setLoading(true); + const toastId = toast.loading("Registering..."); + + const response = await fetch(`${API_BASE_URL}/api/signup`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + firstname: formData.firstname, + lastname: formData.lastname, + email: formData.email, + password: formData.password, + }), + }); + + const data = await response.json(); + + if (response.ok) { + toast.success("Successfully registered!", { id: toastId }); + // Optionally redirect to login + } else { + toast.error(data.message || "Signup failed.", { id: toastId }); + } + } catch (error) { + toast.error("An error occurred. Please try again."); + } finally { + setLoading(false); + } + }; return (

+
-

Sign Up

+

Sign Up

- {/* Form Fields */} -
+
{/* Password Field */}
-
- {/* Sign Up Button */} - + {/* Sign Up Button */} + + {/* Redirect to Login */}

@@ -84,3 +170,92 @@ const SignUp = () => { }; export default SignUp; + + + +// // eslint-disable-next-line no-unused-vars +// import React, { useState } from "react"; +// import { FiEye, FiEyeOff } from "react-icons/fi"; +// import { Link } from "react-router-dom"; + +// const SignUp = () => { +// const [showPassword, setShowPassword] = useState(false); +// const [showConfirmPassword, setShowConfirmPassword] = useState(false); + +// return ( +//

+//
+//

Sign Up

+ +// {/* Form Fields */} +//
+// +// +// + +// {/* Password Field */} +//
+// +// +//
+ +// {/* Confirm Password Field */} +//
+// +// +//
+//
+ +// {/* Sign Up Button */} +// + +// {/* Redirect to Login */} +//

+// Already have an account?{" "} +// +// Login +// +//

+//
+//
+// ); +// }; + +// export default SignUp; From 6294066ea700c956db663ab87809ae51c2278144 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Fri, 18 Apr 2025 18:11:43 +0530 Subject: [PATCH 14/27] Fix:Installed axios --- Frontend/package-lock.json | 115 ++++++++++++++++++++++++++++++++----- Frontend/package.json | 1 + 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index f247e76..91022ce 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.6.0", "@tailwindcss/vite": "^4.0.9", + "axios": "^1.8.4", "lucide-react": "^0.476.0", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -1683,6 +1684,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -1735,6 +1742,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1805,7 +1823,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -1893,6 +1910,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2040,6 +2069,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -2067,7 +2105,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2164,7 +2201,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2173,7 +2209,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2209,7 +2244,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2221,7 +2255,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -2585,6 +2618,26 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -2600,6 +2653,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2630,7 +2698,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2677,7 +2744,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -2701,7 +2767,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -2780,7 +2845,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -2845,7 +2909,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -2857,7 +2920,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -2872,7 +2934,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3689,11 +3750,31 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4017,6 +4098,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index 1cb21f3..04f0317 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -12,6 +12,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.6.0", "@tailwindcss/vite": "^4.0.9", + "axios": "^1.8.4", "lucide-react": "^0.476.0", "react": "^19.0.0", "react-dom": "^19.0.0", From 5c9e8fedbcb93c30cdd78275ef3a7e21769b6292 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:18:21 +0530 Subject: [PATCH 15/27] Feat:Added username from localhost for fetching --- Frontend/src/components/FileList.jsx | 92 +++++++++++++--------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/Frontend/src/components/FileList.jsx b/Frontend/src/components/FileList.jsx index e7647a7..b166337 100644 --- a/Frontend/src/components/FileList.jsx +++ b/Frontend/src/components/FileList.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { useDispatch } from "react-redux"; import { setCurrentPath } from "../store/pathSlice"; @@ -18,6 +18,10 @@ import { ArrowLeft, } from "lucide-react"; +const username = localStorage.getItem("username"); +// const authToken = localStorage.getItem("token"); +const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; +// const token = localStorage.getItem("token"); const FileTable = ({ initialPath }) => { const [currentPath, setCurrentPathState] = useState(initialPath || "/"); const [files, setFiles] = useState([]); @@ -33,6 +37,7 @@ const FileTable = ({ initialPath }) => { const getIcon = (name, type) => { if (type === "Folder") return ; + const ext = name.split(".").pop().toLowerCase(); switch (ext) { case "txt": @@ -74,44 +79,41 @@ const FileTable = ({ initialPath }) => { const fetchFiles = async () => { try { - const response = await fetch( - `http://192.168.29.61:8080/api/hdfs/listFiles?hdfsPath=${currentPath}` + const res = await fetch( + `${API_URL}/api/hdfs/listFiles?hdfsPath=${encodeURIComponent( + currentPath + )}` ); - const data = await response.json(); + const data = await res.json(); + // filter out indented children, only top-level entries const filtered = data.filter( (entry) => entry.match(/^ */)[0].length === 0 ); setFiles(filtered); - } catch (error) { - console.error("Failed to fetch files:", error); + } catch (err) { + console.error("Failed to fetch files:", err); setFiles([]); } }; - const deleteFileOrFolder = async (name, type, event) => { - event.stopPropagation(); // Prevent row onClick from firing. + const deleteFileOrFolder = async (name, type, e) => { + e.stopPropagation(); try { const hdfsPath = currentPath === "/" ? `/${name}` : `${currentPath}/${name}`; - const encodedPath = encodeURIComponent(hdfsPath); - let deleteEndpoint = ""; + const encoded = encodeURIComponent(hdfsPath); + const endpoint = + type === "File" + ? `${API_URL}/api/hdfs/deleteFile?hdfsPath=${encoded}` + : `${API_URL}/api/hdfs/deleteFolder?hdfsPath=${encoded}`; - 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 resp = await fetch(endpoint, { method: "DELETE" }); + if (!resp.ok) { + console.error("Deletion failed:", await resp.text()); } - - 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); + } catch (err) { + console.error("Failed to delete:", err); } }; @@ -134,53 +136,45 @@ const FileTable = ({ initialPath }) => { }; const handleFileDownload = async (hdfsPath, name, event) => { - event.stopPropagation(); // Prevent row click (if any) for files. + event.stopPropagation(); // Prevent row click (if any) try { - const formData = new URLSearchParams(); - formData.append("hdfsPath", hdfsPath); - formData.append("username", "kalas"); + const authToken = localStorage.getItem("token"); // Get JWT token from localStorage const response = await fetch( - "http://192.168.29.61:8080/api/hdfs/downloadFile", + `${API_URL}/api/hdfs/downloadFile?hdfsEncPath=${hdfsPath}&localPath=${name}&username=${username}`, { method: "POST", headers: { - "Content-Type": "application/x-www-form-urlencoded", + Authorization: `Bearer ${authToken}`, }, - body: formData.toString(), } ); - if (!response.ok) throw new Error("Download failed"); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Download failed: ${errorText}`); + } - // Extract filename from header OR fallback to name from path + // Extract filename from response headers or fallback to 'name' const contentDisposition = response.headers.get("Content-Disposition"); - let fileName = "downloaded_file"; - + let downloadedFileName = name; 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; + if (match) downloadedFileName = match[1]; } - // Create blob and trigger download const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); + const blobUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); - link.href = url; - link.download = fileName; + link.href = blobUrl; + link.download = downloadedFileName; document.body.appendChild(link); link.click(); link.remove(); - window.URL.revokeObjectURL(url); + window.URL.revokeObjectURL(blobUrl); + fetchFiles(); } catch (error) { console.error("Download failed:", error); alert("Something went wrong while downloading the file."); @@ -201,6 +195,7 @@ const FileTable = ({ initialPath }) => { )}
+
- File Name - - Type - - Action - NameActions
+ No files found.
+ + {getIcon(name, type)} {name} {type} - {isFile(entry) ? ( - - Download - - ) : ( + + {isFile(entry) && ( )} +
@@ -208,6 +203,7 @@ const FileTable = ({ initialPath }) => { + {files.length === 0 ? ( From c55dd4b661ce1952f33dbbd27b1e621367329cb8 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:19:35 +0530 Subject: [PATCH 16/27] Fix:Fixed the bug where dropdown menu was not appearing and removed uneccessary options --- Frontend/src/components/Sidebar.jsx | 259 +++++++++------------------- 1 file changed, 77 insertions(+), 182 deletions(-) diff --git a/Frontend/src/components/Sidebar.jsx b/Frontend/src/components/Sidebar.jsx index 1b252af..540fe82 100644 --- a/Frontend/src/components/Sidebar.jsx +++ b/Frontend/src/components/Sidebar.jsx @@ -1,21 +1,25 @@ -import { Link } from "react-router-dom"; -import { toast } from 'react-hot-toast'; -import { useNavigate } from 'react-router-dom'; +import { useState, useEffect, useRef } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { toast } from "react-hot-toast"; + const Sidebar = () => { + const navigate = useNavigate(); // Hook for programmatic navigation + const [userMenuOpen, setUserMenuOpen] = useState(false); + const menuRef = useRef(); - const navigate = useNavigate(); // Hook for programmatic navigation - + // Show loading toast and perform logout const handleLogout = () => { - // Show loading toast const loadingToast = toast.loading("Logging out..."); // Simulate a delay (for example, network request) setTimeout(() => { // Remove the token from localStorage - localStorage.removeItem('token'); // Adjust the key if necessary + localStorage.removeItem("token"); + localStorage.removeItem("username"); + localStorage.removeItem("expiresIn"); // Redirect user to the homepage - navigate('/'); + navigate("/"); // Show success toast after logout toast.update(loadingToast, { @@ -24,44 +28,44 @@ const Sidebar = () => { isLoading: false, autoClose: 2000, }); - }, 1500); // Simulate a 1.5 second delay before showing success + }, 1500); }; + // Close dropdown on outside click + useEffect(() => { + const handleClickOutside = (e) => { + if (menuRef.current && !menuRef.current.contains(e.target)) { + setUserMenuOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + return ( <> - + @@ -255,4 +150,4 @@ const Sidebar = () => { ); }; -export default Sidebar; \ No newline at end of file +export default Sidebar; From 608435b75844c764891f8f5d34f5cf16044b68b4 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:21:03 +0530 Subject: [PATCH 17/27] Fix:Fixed the logic because the api endpoints were changed --- Frontend/src/components/FileUploadModal.jsx | 167 ++++++++++++++++---- 1 file changed, 132 insertions(+), 35 deletions(-) diff --git a/Frontend/src/components/FileUploadModal.jsx b/Frontend/src/components/FileUploadModal.jsx index b47341c..bb70631 100644 --- a/Frontend/src/components/FileUploadModal.jsx +++ b/Frontend/src/components/FileUploadModal.jsx @@ -1,81 +1,173 @@ -import React, { useState } from "react"; +import { useState, useEffect } from "react"; import { useSelector } from "react-redux"; +import PropTypes from "prop-types"; const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { const currentPath = useSelector((state) => state.path.currentPath); const [file, setFile] = useState(null); - const [message, setMessage] = useState(""); const [uploading, setUploading] = useState(false); + const [uploadMessage, setUploadMessage] = useState(""); + const [newFolderName, setNewFolderName] = useState(""); + const [creatingFolder, setCreatingFolder] = useState(false); + const [folderMessage, setFolderMessage] = useState(""); + const username = localStorage.getItem("username"); + const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; + useEffect(() => { + const handleEsc = (e) => { + if (e.key === "Escape") onClose(); + }; + document.addEventListener("keydown", handleEsc); + return () => document.removeEventListener("keydown", handleEsc); + }, [onClose]); + + const isFolderNameValid = (name) => { + return /^[a-zA-Z0-9-_ ]+$/.test(name); // disallow special chars like / \ * ? etc. + }; const uploadFileToHDFS = async () => { if (!file) { - setMessage("Please select a file before uploading."); + setUploadMessage("⚠️ Please select a file before uploading."); return; } - const formData = new FormData(); + formData.append("file", file); formData.append("hdfsPath", currentPath); formData.append("uploadedFileName", file.name); - formData.append("username", "kalas"); + formData.append("username", username); try { setUploading(true); - setMessage("Uploading file..."); + setUploadMessage("⏳ Uploading file..."); + const response = await fetch(`${API_URL}/api/hdfs/uploadFile`, { + method: "POST", + body: formData, + }); + if (!response.ok) { + const errorText = await response.text(); + setUploadMessage(`❌ Upload failed: ${errorText}`); + } else { + setUploadMessage("✅ File uploaded successfully!"); + onUploadSuccess(); + setTimeout(() => { + setUploadMessage(""); + onClose(); + }, 1000); + } + } catch (err) { + console.error(err); + setUploadMessage("❌ An error occurred during upload."); + } finally { + setUploading(false); + } + }; + const createFolder = async () => { + if (!newFolderName.trim()) { + setFolderMessage("⚠️ Please enter a folder name."); + return; + } + if (!isFolderNameValid(newFolderName.trim())) { + setFolderMessage("❌ Folder name contains invalid characters."); + return; + } + + try { + setCreatingFolder(true); + setFolderMessage("⏳ Creating folder..."); + const folderPath = + currentPath === "/" ? "" : currentPath.replace(/\/$/, ""); + const newFolderPath = `${folderPath}/${newFolderName}`; + console.log(newFolderPath); const response = await fetch( - `http://192.168.29.61:8080/api/hdfs/uploadFile`, - { method: "POST", body: formData } + `${API_URL}/api/hdfs/createFolder?hdfsPath=${newFolderPath}`, + { method: "POST" } ); if (!response.ok) { const errorText = await response.text(); - setMessage(`Upload failed: ${errorText}`); + setFolderMessage(`❌ Folder creation failed: ${errorText}`); } else { - setMessage("File uploaded successfully!"); - onUploadSuccess(); // Notify Dashboard to refresh file list - setTimeout(onClose, 1000); + setFolderMessage("✅ Folder created successfully!"); + onUploadSuccess(); + setNewFolderName(""); + setTimeout(() => { + setFolderMessage(""); + onClose(); + }, 1000); } - } catch (error) { - console.error(error); - setMessage("An error occurred during upload."); + } catch (err) { + console.error(err); + setFolderMessage("❌ An error occurred during folder creation."); } finally { - setUploading(false); + setCreatingFolder(false); } }; if (!show) return null; return ( -
-
-
-
-

Upload File

+
+
+
+
+

Manage HDFS

-
- setFile(e.target.files[0])} - className="w-full mb-4" - /> - {message &&

{message}

} -
- +
+ {/* File Upload Section */} +
+

+ Upload File +

+ setFile(e.target.files[0])} + className="w-full mb-3 text-sm text-gray-600 file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" + /> + {file && ( +

+ Selected file: {file.name} +

+ )} + {uploadMessage && ( +

{uploadMessage}

+ )} +
+ + {/* Create Folder Section */} +
+

+ Create Folder +

+ setNewFolderName(e.target.value)} + className="w-full mb-3 px-3 py-2 border border-gray-300 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-200" + /> + {folderMessage && ( +

{folderMessage}

+ )} +
@@ -83,5 +175,10 @@ const FileUploadModal = ({ show, onClose, onUploadSuccess }) => {
); }; +FileUploadModal.propTypes = { + show: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onUploadSuccess: PropTypes.func.isRequired, +}; export default FileUploadModal; From 920c793fa66f4a5157e3e83ae448e77277b53fc5 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:22:39 +0530 Subject: [PATCH 18/27] Feat: Added the logic to save the username and token into the localhost alongside the expiryId. Add Login page component in Authentication module --- Frontend/src/pages/Authentication/Login.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Frontend/src/pages/Authentication/Login.jsx b/Frontend/src/pages/Authentication/Login.jsx index 8f755b6..9fa60e0 100644 --- a/Frontend/src/pages/Authentication/Login.jsx +++ b/Frontend/src/pages/Authentication/Login.jsx @@ -1,9 +1,8 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import { FiEye, FiEyeOff } from "react-icons/fi"; import { Link, useNavigate } from "react-router-dom"; import toast from "react-hot-toast"; // Import React Hot Toast - const API_URL = import.meta.env.VITE_API_URL; // Using .env variable const Login = () => { @@ -52,6 +51,14 @@ const Login = () => { // On success, store the token in localStorage localStorage.setItem("token", data.token); localStorage.setItem("expiresIn", data.expiresIn); + fetch(`${API_URL}/api/hdfs/getUsernameByEmail?email=${email}`) + .then((response) => response.text()) + .then((username) => { + localStorage.setItem("username", username); + }) + .catch((error) => { + console.error("Error fetching username:", error); + }); // Show success toast toast.success("Login successful!"); @@ -65,7 +72,7 @@ const Login = () => { } catch (error) { // Dismiss the loading toast and show error toast.dismiss(toastId); - toast.error("An error occurred. Please try again."); + toast.error("An error occurred. Please try again.", error); } finally { setLoading(false); } @@ -144,4 +151,4 @@ const Login = () => { ); }; -export default Login; \ No newline at end of file +export default Login; From d59e8c789cf09f0258da8901d9ac0db1ffd86dc4 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:28:06 +0530 Subject: [PATCH 19/27] Minor changes --- Frontend/src/pages/Authentication/SignUp.jsx | 7 ++----- Frontend/src/pages/UserPages/UploadFile.jsx | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Frontend/src/pages/Authentication/SignUp.jsx b/Frontend/src/pages/Authentication/SignUp.jsx index ab265ce..788340b 100644 --- a/Frontend/src/pages/Authentication/SignUp.jsx +++ b/Frontend/src/pages/Authentication/SignUp.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { FiEye, FiEyeOff } from "react-icons/fi"; import { Link } from "react-router-dom"; import toast, { Toaster } from "react-hot-toast"; @@ -6,7 +6,6 @@ import toast, { Toaster } from "react-hot-toast"; // const API_BASE_URL = process.env.REACT_APP_API_URL; const API_BASE_URL = import.meta.env.VITE_API_URL; - const SignUp = () => { const [formData, setFormData] = useState({ firstname: "", @@ -59,7 +58,7 @@ const SignUp = () => { toast.error(data.message || "Signup failed.", { id: toastId }); } } catch (error) { - toast.error("An error occurred. Please try again."); + toast.error("An error occurred. Please try again.", error); } finally { setLoading(false); } @@ -171,8 +170,6 @@ const SignUp = () => { export default SignUp; - - // // eslint-disable-next-line no-unused-vars // import React, { useState } from "react"; // import { FiEye, FiEyeOff } from "react-icons/fi"; diff --git a/Frontend/src/pages/UserPages/UploadFile.jsx b/Frontend/src/pages/UserPages/UploadFile.jsx index 81bfea9..f227508 100644 --- a/Frontend/src/pages/UserPages/UploadFile.jsx +++ b/Frontend/src/pages/UserPages/UploadFile.jsx @@ -1,11 +1,8 @@ -import React, { useState } from "react"; -// Adjust the import for FileUpload to the correct location: +import { useState } from "react"; import FileUploadModal from "../../components/FileUploadModal"; const UploadFile = () => { - // Control modal visibility with state const [isModalOpen, setIsModalOpen] = useState(false); - // Dummy callback or proper function if necessary const fetchFiles = () => { console.log("Upload succeeded, fetch or update file list here."); }; From 54dd5a1fccf6210297ed0276b004f57c14be8301 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:29:07 +0530 Subject: [PATCH 20/27] Feat:Added logic to redirect the user to login page if user is not logged in --- Frontend/src/pages/UserPages/Dashboard.jsx | 50 ++++++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/Frontend/src/pages/UserPages/Dashboard.jsx b/Frontend/src/pages/UserPages/Dashboard.jsx index 6845f59..7d8ff46 100644 --- a/Frontend/src/pages/UserPages/Dashboard.jsx +++ b/Frontend/src/pages/UserPages/Dashboard.jsx @@ -1,31 +1,52 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; import Sidebar from "../../components/Sidebar"; import FileList from "../../components/FileList"; -import FileUploadModal from "../../components/FileUploadModal"; // renamed the import accordingly - +import FileUploadModal from "../../components/FileUploadModal"; import { FiPlus } from "react-icons/fi"; const Dashboard = () => { const [files, setFiles] = useState([]); - // State to manage upload modal visibility const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + const [error, setError] = useState(""); + const navigate = useNavigate(); + const API_URL = import.meta.env.VITE_API_URL; + const isUserLoggedIn = () => { + const token = localStorage.getItem("token"); + const username = localStorage.getItem("username"); + const expiresIn = localStorage.getItem("expiresIn"); + + if (!token || !username || !expiresIn) return false; + + const expiryTime = new Date(expiresIn).getTime(); + const now = new Date().getTime(); + + if (now > expiryTime) { + localStorage.clear(); + return false; + } + + return true; + }; const fetchFiles = async () => { try { - const response = await fetch( - "http://192.168.29.61:8080/api/hdfs/listFiles?hdfsPath=/" - ); + const response = await fetch(`${API_URL}/api/hdfs/listFiles?hdfsPath=/`); const data = await response.json(); setFiles(data); } catch (error) { console.error("Failed to fetch files:", error); + setError("Failed to load files. Please try again later."); } }; useEffect(() => { - fetchFiles(); - // downloadFile("/sonali/cc.pptx", "kalas"); - }, []); + if (!isUserLoggedIn()) { + navigate("/login"); + } else { + fetchFiles(); + } + }, [navigate]); return ( <> @@ -34,7 +55,6 @@ const Dashboard = () => {

Dashboard

- {/* Use onClick to toggle the modal */}
- + {error ? ( +

{error}

+ ) : ( + + )}
- {/* Render the FileUploadModal with proper props */} setIsUploadModalOpen(false)} onUploadSuccess={() => { fetchFiles(); - // Optionally close the modal after upload success setIsUploadModalOpen(false); }} /> From 0adc932e53472bfbcc16b894a0dd3897b9f13bc8 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:52:33 +0530 Subject: [PATCH 21/27] Feat:Added slice to re-render file table on every file uplaod and folder creation --- Frontend/src/store/UploadStatusSlice.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Frontend/src/store/UploadStatusSlice.js diff --git a/Frontend/src/store/UploadStatusSlice.js b/Frontend/src/store/UploadStatusSlice.js new file mode 100644 index 0000000..570f7fe --- /dev/null +++ b/Frontend/src/store/UploadStatusSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const initialState = { + isUploading: false, +}; + +const uploadStatusSlice = createSlice({ + name: "uploadStatus", + initialState, + reducers: { + setIsUploading: (state) => { + state.isUploading = !state.isUploading; + }, + }, +}); + +export const { setIsUploading } = uploadStatusSlice.actions; +export default uploadStatusSlice.reducer; From c426fecf4380a99be3e6268e07a78e7e7756f84f Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:53:34 +0530 Subject: [PATCH 22/27] Feat:Added the isUploading in the useEffect for re-rendering file table when new file/folder is uploaded --- Frontend/src/components/FileList.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Frontend/src/components/FileList.jsx b/Frontend/src/components/FileList.jsx index b166337..8817562 100644 --- a/Frontend/src/components/FileList.jsx +++ b/Frontend/src/components/FileList.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import PropTypes from "prop-types"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { setCurrentPath } from "../store/pathSlice"; import { FileText, @@ -26,7 +26,7 @@ const FileTable = ({ initialPath }) => { const [currentPath, setCurrentPathState] = useState(initialPath || "/"); const [files, setFiles] = useState([]); const dispatch = useDispatch(); - + const isUploading = useSelector((state) => state.upload.isUploading); const getType = (entry) => entry.trim().startsWith("📁") ? "Folder" : "File"; @@ -120,7 +120,7 @@ const FileTable = ({ initialPath }) => { useEffect(() => { dispatch(setCurrentPath(currentPath)); fetchFiles(); - }, [currentPath]); + }, [currentPath, dispatch, isUploading]); const handleOpenFolder = (folderName) => { const newPath = @@ -164,7 +164,10 @@ const FileTable = ({ initialPath }) => { ); if (match) downloadedFileName = match[1]; } - + console.log( + `${API_URL}/api/hdfs/downloadFile?hdfsEncPath=${hdfsPath}&localPath=${name}&username=${username}`, + authToken + ); const blob = await response.blob(); const blobUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); From febde7dffe2c1853963e1ce7f843767a0ae0d951 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:54:38 +0530 Subject: [PATCH 23/27] Feat:Called UploadStatusSlice for toggling the variable for the re-render --- Frontend/src/components/FileUploadModal.jsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Frontend/src/components/FileUploadModal.jsx b/Frontend/src/components/FileUploadModal.jsx index bb70631..b4aa557 100644 --- a/Frontend/src/components/FileUploadModal.jsx +++ b/Frontend/src/components/FileUploadModal.jsx @@ -1,9 +1,12 @@ import { useState, useEffect } from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import PropTypes from "prop-types"; +import { setIsUploading } from "../store/UploadStatusSlice"; const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { const currentPath = useSelector((state) => state.path.currentPath); + const dispatch = useDispatch(); + const isUploading = useSelector((state) => state.upload.isUploading); const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [uploadMessage, setUploadMessage] = useState(""); @@ -12,11 +15,13 @@ const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { const [folderMessage, setFolderMessage] = useState(""); const username = localStorage.getItem("username"); const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; + useEffect(() => { const handleEsc = (e) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handleEsc); + return () => document.removeEventListener("keydown", handleEsc); }, [onClose]); @@ -48,7 +53,8 @@ const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { setUploadMessage(`❌ Upload failed: ${errorText}`); } else { setUploadMessage("✅ File uploaded successfully!"); - onUploadSuccess(); + dispatch(setIsUploading(true)); // Dispatch the action to set isUploading to true + onUploadSuccess(); // Call the onUploadSuccess prop to notify the parent setTimeout(() => { setUploadMessage(""); onClose(); @@ -89,7 +95,8 @@ const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { setFolderMessage(`❌ Folder creation failed: ${errorText}`); } else { setFolderMessage("✅ Folder created successfully!"); - onUploadSuccess(); + dispatch(setIsUploading(true)); // Dispatch the action to set isUploading to true + onUploadSuccess(currentPath); // Call the onUploadSuccess prop after folder creation too setNewFolderName(""); setTimeout(() => { setFolderMessage(""); From 96fc18ab8098a125fbe8f76ba49470b613a97d89 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:56:22 +0530 Subject: [PATCH 24/27] Fix:Removed this unused file --- Frontend/src/pages/UserPages/UploadFile.jsx | 54 --------------------- 1 file changed, 54 deletions(-) delete mode 100644 Frontend/src/pages/UserPages/UploadFile.jsx diff --git a/Frontend/src/pages/UserPages/UploadFile.jsx b/Frontend/src/pages/UserPages/UploadFile.jsx deleted file mode 100644 index f227508..0000000 --- a/Frontend/src/pages/UserPages/UploadFile.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useState } from "react"; -import FileUploadModal from "../../components/FileUploadModal"; -const UploadFile = () => { - const [isModalOpen, setIsModalOpen] = useState(false); - - const fetchFiles = () => { - console.log("Upload succeeded, fetch or update file list here."); - }; - - return ( - <> - {/* Button or trigger to open the modal */} - - -
-
-
-
-

- Upload File -

- -
-
- -
-
-
-
- - ); -}; - -export default UploadFile; From afccce0be34b53476e83bebf4a898623b5c6b657 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:56:57 +0530 Subject: [PATCH 25/27] Feat:Added the upload reducer in the store for re-renders --- Frontend/src/store/store.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Frontend/src/store/store.js b/Frontend/src/store/store.js index 0ded312..5f5a518 100644 --- a/Frontend/src/store/store.js +++ b/Frontend/src/store/store.js @@ -1,9 +1,11 @@ // src/redux/store.js import { configureStore } from "@reduxjs/toolkit"; import pathReducer from "./pathSlice"; +import setIsUploadingReducer from "./UploadStatusSlice"; export const store = configureStore({ reducer: { path: pathReducer, + upload: setIsUploadingReducer, }, }); From 1f6cbf43104f15d7a322056e63a8ae99955e1706 Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 20:04:57 +0530 Subject: [PATCH 26/27] Feat: Update API URL and refactor FileTable component for improved path handling --- Frontend/.env | 2 +- Frontend/src/components/FileList.jsx | 87 +++++++++++----------------- Frontend/src/utils/api.js | 34 ----------- 3 files changed, 35 insertions(+), 88 deletions(-) delete mode 100644 Frontend/src/utils/api.js diff --git a/Frontend/.env b/Frontend/.env index eea892b..77b2686 100644 --- a/Frontend/.env +++ b/Frontend/.env @@ -1 +1 @@ -VITE_API_URL=http://192.168.29.61:8080 +VITE_API_URL=http://192.168.29.61:8081 diff --git a/Frontend/src/components/FileList.jsx b/Frontend/src/components/FileList.jsx index 8817562..03ed7b8 100644 --- a/Frontend/src/components/FileList.jsx +++ b/Frontend/src/components/FileList.jsx @@ -18,20 +18,24 @@ import { ArrowLeft, } from "lucide-react"; -const username = localStorage.getItem("username"); -// const authToken = localStorage.getItem("token"); const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; -// const token = localStorage.getItem("token"); + const FileTable = ({ initialPath }) => { - const [currentPath, setCurrentPathState] = useState(initialPath || "/"); + // Read username dynamically to avoid stale null on first load + const username = localStorage.getItem("username") || ""; + const userRoot = `/${username}`; + + // Initialize currentPath only once with the correct userRoot + const [currentPath, setCurrentPathState] = useState( + () => initialPath || userRoot + ); const [files, setFiles] = useState([]); const dispatch = useDispatch(); const isUploading = useSelector((state) => state.upload.isUploading); + const getType = (entry) => entry.trim().startsWith("📁") ? "Folder" : "File"; - const getName = (entry) => entry.trim().replace(/^📁\s*|^📄\s*/, ""); - const isFile = (entry) => getType(entry) === "File"; const getIcon = (name, type) => { @@ -85,7 +89,6 @@ const FileTable = ({ initialPath }) => { )}` ); const data = await res.json(); - // filter out indented children, only top-level entries const filtered = data.filter( (entry) => entry.match(/^ */)[0].length === 0 ); @@ -99,18 +102,18 @@ const FileTable = ({ initialPath }) => { const deleteFileOrFolder = async (name, type, e) => { e.stopPropagation(); try { - const hdfsPath = - currentPath === "/" ? `/${name}` : `${currentPath}/${name}`; - const encoded = encodeURIComponent(hdfsPath); + const hdfsPath = `${currentPath}/${name}`; const endpoint = type === "File" - ? `${API_URL}/api/hdfs/deleteFile?hdfsPath=${encoded}` - : `${API_URL}/api/hdfs/deleteFolder?hdfsPath=${encoded}`; + ? `${API_URL}/api/hdfs/deleteFile?hdfsPath=${encodeURIComponent( + hdfsPath + )}` + : `${API_URL}/api/hdfs/deleteFolder?hdfsPath=${encodeURIComponent( + hdfsPath + )}`; const resp = await fetch(endpoint, { method: "DELETE" }); - if (!resp.ok) { - console.error("Deletion failed:", await resp.text()); - } + if (!resp.ok) console.error("Deletion failed:", await resp.text()); fetchFiles(); } catch (err) { console.error("Failed to delete:", err); @@ -123,60 +126,40 @@ const FileTable = ({ initialPath }) => { }, [currentPath, dispatch, isUploading]); const handleOpenFolder = (folderName) => { - const newPath = - currentPath === "/" ? `/${folderName}` : `${currentPath}/${folderName}`; - setCurrentPathState(newPath); + setCurrentPathState((prev) => `${prev}/${folderName}`); }; const goBack = () => { - if (currentPath === "/") return; + if (currentPath === userRoot) return; const parts = currentPath.split("/").filter(Boolean); parts.pop(); - setCurrentPathState(parts.length === 0 ? "/" : `/${parts.join("/")}`); + setCurrentPathState(parts.length === 0 ? userRoot : `/${parts.join("/")}`); }; const handleFileDownload = async (hdfsPath, name, event) => { - event.stopPropagation(); // Prevent row click (if any) + event.stopPropagation(); try { - const authToken = localStorage.getItem("token"); // Get JWT token from localStorage - + const authToken = localStorage.getItem("token"); const response = await fetch( - `${API_URL}/api/hdfs/downloadFile?hdfsEncPath=${hdfsPath}&localPath=${name}&username=${username}`, + `${API_URL}/api/hdfs/downloadFile?hdfsEncPath=${encodeURIComponent( + hdfsPath + )}&localPath=${name}&username=${username}`, { method: "POST", - headers: { - Authorization: `Bearer ${authToken}`, - }, + headers: { Authorization: `Bearer ${authToken}` }, } ); - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Download failed: ${errorText}`); - } - - // Extract filename from response headers or fallback to 'name' - const contentDisposition = response.headers.get("Content-Disposition"); - let downloadedFileName = name; - if (contentDisposition && contentDisposition.includes("filename=")) { - const match = contentDisposition.match( - /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/ - ); - if (match) downloadedFileName = match[1]; - } - console.log( - `${API_URL}/api/hdfs/downloadFile?hdfsEncPath=${hdfsPath}&localPath=${name}&username=${username}`, - authToken - ); + if (!response.ok) throw new Error(await response.text()); const blob = await response.blob(); - const blobUrl = window.URL.createObjectURL(blob); + const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); - link.href = blobUrl; - link.download = downloadedFileName; + link.href = url; + link.download = name; document.body.appendChild(link); link.click(); link.remove(); - window.URL.revokeObjectURL(blobUrl); + window.URL.revokeObjectURL(url); fetchFiles(); } catch (error) { console.error("Download failed:", error); @@ -188,7 +171,7 @@ const FileTable = ({ initialPath }) => {
Path: {currentPath} - {currentPath !== "/" && ( + {currentPath !== userRoot && (
- {files.length === 0 ? ( @@ -218,8 +200,7 @@ const FileTable = ({ initialPath }) => { files.map((entry, idx) => { const name = getName(entry); const type = getType(entry); - const hdfsPath = - currentPath === "/" ? `/${name}` : `${currentPath}/${name}`; + const hdfsPath = `${currentPath}/${name}`; return ( { - const formData = new FormData(); - formData.append("file", file); - formData.append("hdfsPath", "/kalas"); - formData.append("uploadedFileName", uploadedFileName); - formData.append("username", "kalas"); - - try { - const response = await fetch( - "http://192.168.29.61:8080/api/hdfs/uploadFile", - { - method: "POST", - body: formData, - } - ); - - if (response.ok) { - const data = await response.json(); // or response.json() if JSON is returned - - console.log("Response:", data); - } else { - console.error("Upload failed with status:", response.status); - } - } catch (error) { - console.error("Error uploading file:", error); - alert("An error occurred while uploading the file."); - } -}; From 2c29597f1d4b1c6e770cc4837337a64ca6abea7c Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sat, 19 Apr 2025 20:06:25 +0530 Subject: [PATCH 27/27] {new_commit_message} --- Frontend/src/components/FileUploadModal.jsx | 3 +- Frontend/src/pages/Authentication/SignUp.jsx | 145 +++++-------------- 2 files changed, 40 insertions(+), 108 deletions(-) diff --git a/Frontend/src/components/FileUploadModal.jsx b/Frontend/src/components/FileUploadModal.jsx index b4aa557..54ac590 100644 --- a/Frontend/src/components/FileUploadModal.jsx +++ b/Frontend/src/components/FileUploadModal.jsx @@ -6,7 +6,6 @@ import { setIsUploading } from "../store/UploadStatusSlice"; const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { const currentPath = useSelector((state) => state.path.currentPath); const dispatch = useDispatch(); - const isUploading = useSelector((state) => state.upload.isUploading); const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [uploadMessage, setUploadMessage] = useState(""); @@ -14,7 +13,7 @@ const FileUploadModal = ({ show, onClose, onUploadSuccess }) => { const [creatingFolder, setCreatingFolder] = useState(false); const [folderMessage, setFolderMessage] = useState(""); const username = localStorage.getItem("username"); - const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; + const API_URL = import.meta.env.VITE_API_URL; useEffect(() => { const handleEsc = (e) => { diff --git a/Frontend/src/pages/Authentication/SignUp.jsx b/Frontend/src/pages/Authentication/SignUp.jsx index 788340b..3c75291 100644 --- a/Frontend/src/pages/Authentication/SignUp.jsx +++ b/Frontend/src/pages/Authentication/SignUp.jsx @@ -1,12 +1,13 @@ import { useState } from "react"; import { FiEye, FiEyeOff } from "react-icons/fi"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import toast, { Toaster } from "react-hot-toast"; -// const API_BASE_URL = process.env.REACT_APP_API_URL; -const API_BASE_URL = import.meta.env.VITE_API_URL; +const API_URL = import.meta.env.VITE_API_URL; const SignUp = () => { + const navigate = useNavigate(); + const [formData, setFormData] = useState({ firstname: "", lastname: "", @@ -14,7 +15,6 @@ const SignUp = () => { password: "", confirmPassword: "", }); - const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [loading, setLoading] = useState(false); @@ -34,11 +34,12 @@ const SignUp = () => { return; } - try { - setLoading(true); - const toastId = toast.loading("Registering..."); + setLoading(true); + const toastId = toast.loading("Registering..."); - const response = await fetch(`${API_BASE_URL}/api/signup`, { + try { + // 1️⃣ Sign up the user + const signupRes = await fetch(`${API_URL}/api/signup`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -48,17 +49,37 @@ const SignUp = () => { password: formData.password, }), }); + const signupData = await signupRes.json(); - const data = await response.json(); - - if (response.ok) { - toast.success("Successfully registered!", { id: toastId }); - // Optionally redirect to login - } else { - toast.error(data.message || "Signup failed.", { id: toastId }); + if (!signupRes.ok) { + toast.error(signupData.message || "Signup failed.", { id: toastId }); + return; } + + // 2️⃣ Build username and create HDFS folder + const username = + `${formData.firstname}${formData.lastname}`.toLowerCase(); + const folderRes = await fetch( + `${API_URL}/api/hdfs/createFolder?hdfsPath=/${username}`, + { method: "POST" } + ); + + if (!folderRes.ok) { + // you might choose to roll back user creation or just notify + toast.error("Failed to create user folder.", { id: toastId }); + } else { + toast.success("Successfully registered and folder created!", { + id: toastId, + }); + } + + // 3️⃣ Redirect to login after a short delay + setTimeout(() => { + navigate("/login"); + }, 1500); } catch (error) { - toast.error("An error occurred. Please try again.", error); + console.error(error); + toast.error("An error occurred. Please try again.", { id: toastId }); } finally { setLoading(false); } @@ -69,7 +90,6 @@ const SignUp = () => {

Sign Up

-
{ /> -//
- -// {/* Confirm Password Field */} -//
-// -// -//
-// - -// {/* Sign Up Button */} -// - -// {/* Redirect to Login */} -//

-// Already have an account?{" "} -// -// Login -// -//

-// -// -// ); -// }; - -// export default SignUp;
Actions
Actions