From dfd36222da89a216d69c43a92409e8df0ca6f42b Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 23 Jun 2025 11:26:59 +0200 Subject: [PATCH 001/176] setting up my pages --- frontend/src/pages/Dashboard.jsx | 0 frontend/src/pages/Landing.jsx | 0 frontend/src/pages/Login.jsx | 0 frontend/src/pages/Register.jsx | 0 frontend/src/pages/Suggestions.jsx | 0 frontend/src/pages/Upload.jsx | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 frontend/src/pages/Dashboard.jsx create mode 100644 frontend/src/pages/Landing.jsx create mode 100644 frontend/src/pages/Login.jsx create mode 100644 frontend/src/pages/Register.jsx create mode 100644 frontend/src/pages/Suggestions.jsx create mode 100644 frontend/src/pages/Upload.jsx diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/pages/Suggestions.jsx b/frontend/src/pages/Suggestions.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx new file mode 100644 index 0000000000..e69de29bb2 From 00a0cc1dcf296a4c63eff2cd4b42da0e1a6b6f57 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 23 Jun 2025 11:33:43 +0200 Subject: [PATCH 002/176] updating the readme --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 31466b54c2..ce7dc77c87 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -# Final Project +# Final Bootcamp Project - DeskForge -Replace this readme with your own information about your project. +The assignment was to create a full-stack React app that solves a clear problem. -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +I decided to create an app that helps hobbyists, specifically warhammer 40k, to organise their workspaces by uploading a photo of their desk space and getting AI-generated suggestions. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +Complete this later: +[Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?] ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. \ No newline at end of file +Netfliy link: N/A (not live yet) From f99520df0bf0b5a979c0a2194ca123ef695e7a31 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 23 Jun 2025 12:01:55 +0200 Subject: [PATCH 003/176] setting up routes --- frontend/src/App.jsx | 19 +++++++++++++++---- frontend/src/NotFound.jsx | 3 +++ frontend/src/pages/Dashboard.jsx | 3 +++ frontend/src/pages/Landing.jsx | 3 +++ frontend/src/pages/Login.jsx | 3 +++ frontend/src/pages/Register.jsx | 3 +++ frontend/src/pages/Suggestions.jsx | 3 +++ frontend/src/pages/Upload.jsx | 3 +++ package.json | 6 +++++- 9 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 frontend/src/NotFound.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0a24275e6e..edf766dbea 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,8 +1,19 @@ -export const App = () => { +import { Route, Routes, BrowserRouter, Link } from "react-router-dom"; +import Landing from "./pages/Landing"; +import Dashboard from "./pages/Dashboard"; +import Login from "./pages/Login"; +import Register from "./pages/Register"; +import Suggestions from "./pages/Suggestions"; +export const App = () => { return ( - <> -

Welcome to Final Project!

- + + } /> + } /> + } /> + } /> + } /> + } /> + ); }; diff --git a/frontend/src/NotFound.jsx b/frontend/src/NotFound.jsx new file mode 100644 index 0000000000..6277337383 --- /dev/null +++ b/frontend/src/NotFound.jsx @@ -0,0 +1,3 @@ +const NotFound = () => {}; + +export default NotFound; diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index e69de29bb2..45b0ea180a 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -0,0 +1,3 @@ +const Dashboard = () => {}; + +export default Dashboard; diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index e69de29bb2..f8c71983c3 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -0,0 +1,3 @@ +const Landing = () => {}; + +export default Landing; diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index e69de29bb2..4379f316dd 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -0,0 +1,3 @@ +const Login = () => {}; + +export default Login; diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index e69de29bb2..edabb5396f 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -0,0 +1,3 @@ +const Register = () => {}; + +export default Register; diff --git a/frontend/src/pages/Suggestions.jsx b/frontend/src/pages/Suggestions.jsx index e69de29bb2..2958506505 100644 --- a/frontend/src/pages/Suggestions.jsx +++ b/frontend/src/pages/Suggestions.jsx @@ -0,0 +1,3 @@ +const Suggestions = () => {}; + +export default Suggestions; diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx index e69de29bb2..32e6e0358f 100644 --- a/frontend/src/pages/Upload.jsx +++ b/frontend/src/pages/Upload.jsx @@ -0,0 +1,3 @@ +const Upload = () => {}; + +export default Upload; diff --git a/package.json b/package.json index 680d190772..219827d2da 100644 --- a/package.json +++ b/package.json @@ -3,5 +3,9 @@ "version": "1.0.0", "scripts": { "postinstall": "npm install --prefix backend" + }, + "dependencies": { + "react-router": "^7.6.2", + "react-router-dom": "^7.6.2" } -} \ No newline at end of file +} From b748d357e3b480247f29162729f88822711aaf38 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 23 Jun 2025 14:19:20 +0200 Subject: [PATCH 004/176] removing un-needed files and adding the notfound page --- frontend/src/App.jsx | 2 ++ frontend/src/assets/boiler-plate.svg | 18 ---------------- frontend/src/assets/react.svg | 1 - frontend/src/assets/technigo-logo.svg | 31 --------------------------- frontend/src/{ => pages}/NotFound.jsx | 0 5 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 frontend/src/assets/boiler-plate.svg delete mode 100644 frontend/src/assets/react.svg delete mode 100644 frontend/src/assets/technigo-logo.svg rename frontend/src/{ => pages}/NotFound.jsx (100%) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index edf766dbea..f20fd1159d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,6 +4,7 @@ import Dashboard from "./pages/Dashboard"; import Login from "./pages/Login"; import Register from "./pages/Register"; import Suggestions from "./pages/Suggestions"; +import NotFound from "./pages/NotFound"; export const App = () => { return ( @@ -14,6 +15,7 @@ export const App = () => { } /> } /> } /> + } /> ); }; diff --git a/frontend/src/assets/boiler-plate.svg b/frontend/src/assets/boiler-plate.svg deleted file mode 100644 index c9252833b4..0000000000 --- a/frontend/src/assets/boiler-plate.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9bb3..0000000000 --- a/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/assets/technigo-logo.svg b/frontend/src/assets/technigo-logo.svg deleted file mode 100644 index 3f0da3e572..0000000000 --- a/frontend/src/assets/technigo-logo.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/NotFound.jsx b/frontend/src/pages/NotFound.jsx similarity index 100% rename from frontend/src/NotFound.jsx rename to frontend/src/pages/NotFound.jsx From e813557a74c8a1d2e493ae18a36fa0aa06663e7a Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 14:31:33 +0200 Subject: [PATCH 005/176] setting up folders for authorisation and setting up the user model --- backend/middlewares/auth.js | 0 backend/models/User.js | 27 +++++++++++++++++++++++++++ backend/routes/auth.js | 0 package.json | 2 ++ 4 files changed, 29 insertions(+) create mode 100644 backend/middlewares/auth.js create mode 100644 backend/models/User.js create mode 100644 backend/routes/auth.js diff --git a/backend/middlewares/auth.js b/backend/middlewares/auth.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/models/User.js b/backend/models/User.js new file mode 100644 index 0000000000..b63cc02042 --- /dev/null +++ b/backend/models/User.js @@ -0,0 +1,27 @@ +import bcrypt from "bcrypt"; +import mongoose from "mongoose"; + +const userSchema = new mongoose.Schema({ + username: { type: String, required: true, unique: true, minlength: 3 }, + email: { type: String, required: true, unique: true, lowercase: true }, + password: { type: String, required: true, minlength: 6 }, +}); + +userSchema.pre("save", async function (next) { + if (!this.isModified("password")) return next(); + + try { + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); + next(); + } catch (error) { + next(error); + } +}); + +userSchema.methods.comparePassword = async function (candidatePassword) { + return bcrypt.compare(candidatePassword, this.password); +}; + +const User = mongoose.model("User", userSchema); +export default User; diff --git a/backend/routes/auth.js b/backend/routes/auth.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package.json b/package.json index 219827d2da..937ac9e5eb 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "postinstall": "npm install --prefix backend" }, "dependencies": { + "bcrypt": "^6.0.0", + "mongoose": "^8.16.0", "react-router": "^7.6.2", "react-router-dom": "^7.6.2" } From 673b47c606da460583a29bf31feb4bb70b2a6cd0 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 14:34:34 +0200 Subject: [PATCH 006/176] setting up the auth.js route --- backend/routes/auth.js | 73 ++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 75 insertions(+) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index e69de29bb2..f10f6a84b1 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -0,0 +1,73 @@ +import express from "express"; +import jwt from "jsonwebtoken"; + +import User from "../models/User.js"; + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET; + +// signing up as a user +router.post("/signup", async (req, res) => { + const { username, email, password } = req.body; + + try { + const existingUser = await User.findOne({ $or: [{ email }, { username }] }); + if (existingUser) { + return res + .status(400) + .json({ success: false, message: "Username or email already exists." }); + } + + const newUser = new User({ username, email, password }); + await newUser.save(); + + const token = jwt.sign( + { id: newUser._id, username: newUser.username }, + JWT_SECRET, + { + expiresIn: "7d", + } + ); + + res.status(201).json({ success: true, message: "User created", token }); + } catch (error) { + res + .status(500) + .json({ success: false, message: "Error creating user", error }); + } +}); + +// login +router.post("/login", async (req, res) => { + const { email, password } = req.body; + + try { + const user = await User.findOne({ email }); + if (!user) { + return res + .status(401) + .json({ success: false, message: "Invalid email or password" }); + } + + const isMatch = await user.comparePassword(password); + if (!isMatch) { + return res + .status(401) + .json({ success: false, message: "Invalid email or password" }); + } + + const token = jwt.sign( + { id: user._id, username: user.username }, + JWT_SECRET, + { + expiresIn: "7d", + } + ); + + res.status(200).json({ success: true, message: "Logged in", token }); + } catch (error) { + res.status(500).json({ success: false, message: "Login error", error }); + } +}); + +export default router; diff --git a/package.json b/package.json index 937ac9e5eb..32247a931c 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ }, "dependencies": { "bcrypt": "^6.0.0", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.16.0", "react-router": "^7.6.2", "react-router-dom": "^7.6.2" From 02224f9572990f2dfcc86cdc6df98cacd27ee0ed Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 14:35:53 +0200 Subject: [PATCH 007/176] setting up the auth.js middleware --- backend/middlewares/auth.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backend/middlewares/auth.js b/backend/middlewares/auth.js index e69de29bb2..4845c72d8a 100644 --- a/backend/middlewares/auth.js +++ b/backend/middlewares/auth.js @@ -0,0 +1,22 @@ +import jwt from "jsonwebtoken"; + +const JWT_SECRET = process.env.JWT_SECRET; + +export const authenticate = (req, res, next) => { + const authHeader = req.headers.authorization; + + if (!authHeader) + return res.status(401).json({ message: "Authorisation header missing" }); + + const token = authHeader.split(" ")[1]; + + if (!token) return res.status(401).json({ message: "Token missing" }); + + try { + const decoded = jwt.verify(token, JWT_SECRET); + req.user = decoded; + next(); + } catch (error) { + res.status(401).json({ message: "Invalid or expired token" }); + } +}; From 5c6a520c9d951a4697b3650ff040003115ef0d5d Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 14:48:47 +0200 Subject: [PATCH 008/176] connecting to my mongo database --- backend/middlewares/auth.js | 4 +++- backend/server.js | 15 +++++++++++---- package.json | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/backend/middlewares/auth.js b/backend/middlewares/auth.js index 4845c72d8a..b3a95de427 100644 --- a/backend/middlewares/auth.js +++ b/backend/middlewares/auth.js @@ -2,7 +2,7 @@ import jwt from "jsonwebtoken"; const JWT_SECRET = process.env.JWT_SECRET; -export const authenticate = (req, res, next) => { +const authenticate = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) @@ -20,3 +20,5 @@ export const authenticate = (req, res, next) => { res.status(401).json({ message: "Invalid or expired token" }); } }; + +export default authenticate; diff --git a/backend/server.js b/backend/server.js index 070c875189..37aa3f99e4 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,14 +1,21 @@ -import express from "express"; import cors from "cors"; +import dotenv from "dotenv"; +import express from "express"; +import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; -mongoose.connect(mongoUrl); -mongoose.Promise = Promise; +import authenticate from "./middlewares/auth.js"; +import authRoutes from "./routes/auth.js"; const port = process.env.PORT || 8080; const app = express(); +// MONGODB CONNECTION // +const mongoUrl = process.env.MONGO_URL; +mongoose.connect(mongoUrl); +mongoose.Promise = Promise; + +// MIDDLEWARES // app.use(cors()); app.use(express.json()); diff --git a/package.json b/package.json index 32247a931c..8c9dd742c8 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dependencies": { "bcrypt": "^6.0.0", "express": "^5.1.0", + "express-list-endpoints": "^7.1.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.16.0", "react-router": "^7.6.2", From ebb021bfe3a5f4a1fe86838f6ee989536fae67fb Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:00:45 +0200 Subject: [PATCH 009/176] fixing some file changes and npm installs --- backend/package.json | 9 ++++++--- package.json | 16 ---------------- 2 files changed, 6 insertions(+), 19 deletions(-) delete mode 100644 package.json diff --git a/backend/package.json b/backend/package.json index 08f29f2448..3d1277ca94 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,8 +13,11 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.4.0", + "dotenv": "^16.5.0", + "express": "^4.21.2", + "express-list-endpoints": "^7.1.1", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.16.0", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/package.json b/package.json deleted file mode 100644 index 8c9dd742c8..0000000000 --- a/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "project-final-parent", - "version": "1.0.0", - "scripts": { - "postinstall": "npm install --prefix backend" - }, - "dependencies": { - "bcrypt": "^6.0.0", - "express": "^5.1.0", - "express-list-endpoints": "^7.1.1", - "jsonwebtoken": "^9.0.2", - "mongoose": "^8.16.0", - "react-router": "^7.6.2", - "react-router-dom": "^7.6.2" - } -} From d04b1fd6e5520e1cad1c236dcb685e69e1fdaf81 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:03:20 +0200 Subject: [PATCH 010/176] fixing dotenc config --- backend/package.json | 1 + backend/server.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/backend/package.json b/backend/package.json index 3d1277ca94..2753e6a5c9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,6 +12,7 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.21.2", diff --git a/backend/server.js b/backend/server.js index 37aa3f99e4..35674bfde0 100644 --- a/backend/server.js +++ b/backend/server.js @@ -7,6 +7,8 @@ import mongoose from "mongoose"; import authenticate from "./middlewares/auth.js"; import authRoutes from "./routes/auth.js"; +dotenv.config(); + const port = process.env.PORT || 8080; const app = express(); From 00de23a45edf7036af40b526f98a81670e7face5 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:24:58 +0200 Subject: [PATCH 011/176] setting up login and connecting it --- frontend/index.html | 4 +- frontend/package.json | 3 +- frontend/src/App.jsx | 21 ++++++---- frontend/src/pages/Landing.jsx | 24 ++++++++++- frontend/src/pages/Login.jsx | 77 +++++++++++++++++++++++++++++++++- 5 files changed, 115 insertions(+), 14 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 664410b5b9..1095585db1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - Technigo React Vite Boiler Plate + DeskForge
diff --git a/frontend/package.json b/frontend/package.json index 7b2747e949..baee7e29c5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.6.2" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f20fd1159d..fdfb859d8b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,18 +4,21 @@ import Dashboard from "./pages/Dashboard"; import Login from "./pages/Login"; import Register from "./pages/Register"; import Suggestions from "./pages/Suggestions"; +import Upload from "./pages/Upload"; import NotFound from "./pages/NotFound"; export const App = () => { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); }; diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index f8c71983c3..6532d932e7 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -1,3 +1,25 @@ -const Landing = () => {}; +import { Route, Routes, BrowserRouter, Link } from "react-router-dom"; + +const Landing = () => { + return ( + <> +

DeskForge

+

+ Welcome to Desk Forge! The app designed for tabletop gamers and + hobbyists to effortlessly organise their desk spaces. Whether you're + managing your Warhammer armies or juggling multiple creative projects, + Desk Forge helps you stay focused and inspired. Upload photos, add + notes, get AI-powered suggestions, and spend more time doing what you + love instead of stressing about organisation! +

+
+ + + + +
+ + ); +}; export default Landing; diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 4379f316dd..90f4cb03a8 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -1,3 +1,78 @@ -const Login = () => {}; +import { useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; + +// change this once on render +const apiUrl = "http://localhost:8080"; + +const Login = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + + const handleLogin = async (e) => { + e.preventDefault(); + setIsLoading(true); + + try { + const response = await fetch(`${apiUrl}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || "Login failed"); + } + + localStorage.setItem("token", data.token); + + navigate("/app"); + } catch (error) { + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+

Log in

+
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ + {error &&

{error}

} +
+ +

+ Don’t have an account? Register here +

+ ← Back to Home + {isLoading &&

Logging in...

} +
+
+ ); +}; export default Login; From 3f1c59cb39c61e9f7cb5fcba40ee75a4db25be28 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:32:46 +0200 Subject: [PATCH 012/176] setting up registration page --- backend/routes/auth.js | 2 +- frontend/src/pages/Landing.jsx | 4 +- frontend/src/pages/Register.jsx | 86 ++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index f10f6a84b1..a06fdb8a59 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -7,7 +7,7 @@ const router = express.Router(); const JWT_SECRET = process.env.JWT_SECRET; // signing up as a user -router.post("/signup", async (req, res) => { +router.post("/register", async (req, res) => { const { username, email, password } = req.body; try { diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index 6532d932e7..79a41f360b 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -16,7 +16,9 @@ const Landing = () => { - + + + ); diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index edabb5396f..aadc57b5ac 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -1,3 +1,87 @@ -const Register = () => {}; +// TODO: check if email already exists, if it does give a message and ask them to log in instead + +import { useState } from "react"; +import { Link } from "react-router-dom"; + +// change this once on render +const apiUrl = "http://localhost:8080"; + +const Register = () => { + const [name, setName] = useState(""); + const [password, setPassword] = useState(""); + const [email, setEmail] = useState(""); + const [error, setError] = useState(""); + + const handleRegister = async (e) => { + e.preventDefault(); + + try { + const response = await fetch(`${apiUrl}/auth/register`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, email, password }), + }); + + if (!response.ok) { + throw new Error("Registration failed"); + } + + alert("Registration successful!"); + setName(""); + setEmail(""); + setPassword(""); + setError(""); + } catch (error) { + setError(error.message); + } + }; + + return ( +
+
+

Create an Account

+
+
+ + setName(e.target.value)} + /> +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ + {error &&

{error}

} +
+ +

+ Already have an account? Log in +

+ ← Back to Home +
+
+ ); +}; export default Register; From 8e44b63303430be117fb8a0c385eff8e237e3b4a Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:36:09 +0200 Subject: [PATCH 013/176] typo fix --- frontend/src/pages/Register.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index aadc57b5ac..8171c55f1d 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -19,7 +19,7 @@ const Register = () => { const response = await fetch(`${apiUrl}/auth/register`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ username, email, password }), + body: JSON.stringify({ name, email, password }), }); if (!response.ok) { From 368ede3df2e3bdd39def92f93edc68d79b0d4daf Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 15:37:04 +0200 Subject: [PATCH 014/176] todo message added --- frontend/src/pages/Login.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 90f4cb03a8..07467f1d54 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -1,3 +1,5 @@ +// TODO: check if an email doesnt exist as an account, id not, give a message that the accoutn doesnt exist and to register + import { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; From 0178461142617f9eaf3c47c5dc6c7f8f41d5ce7d Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 16:24:37 +0200 Subject: [PATCH 015/176] setting up tailwind --- frontend/package.json | 7 ++++++- frontend/src/index.css | 1 + frontend/vite.config.js | 9 +++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index baee7e29c5..5528e9dac4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,18 +10,23 @@ "preview": "vite preview" }, "dependencies": { + "@tailwindcss/vite": "^4.1.10", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^7.6.2" + "react-router-dom": "^7.6.2", + "tailwind": "^4.0.0" }, "devDependencies": { "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.0.3", + "autoprefixer": "^10.4.21", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.10", "vite": "^6.3.5" } } diff --git a/frontend/src/index.css b/frontend/src/index.css index e69de29bb2..f1d8c73cdc 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 5a33944a9b..356897e39c 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,7 +1,8 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react(), tailwindcss()], +}); From a344b319e86fdec3fa2f764af103368b0df28836 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 16:25:08 +0200 Subject: [PATCH 016/176] removing unnecessary imports --- frontend/src/App.jsx | 2 +- frontend/src/pages/Landing.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fdfb859d8b..20eb288dbc 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,4 @@ -import { Route, Routes, BrowserRouter, Link } from "react-router-dom"; +import { Route, Routes, BrowserRouter } from "react-router-dom"; import Landing from "./pages/Landing"; import Dashboard from "./pages/Dashboard"; import Login from "./pages/Login"; diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index 79a41f360b..c232798f96 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -1,4 +1,4 @@ -import { Route, Routes, BrowserRouter, Link } from "react-router-dom"; +import { Link } from "react-router-dom"; const Landing = () => { return ( From bbf7fd4f1a92fb0e65d643d314a8f303301de787 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 16:34:21 +0200 Subject: [PATCH 017/176] basic styling --- frontend/src/pages/Landing.jsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index c232798f96..2f13557019 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -2,9 +2,9 @@ import { Link } from "react-router-dom"; const Landing = () => { return ( - <> -

DeskForge

-

+

+

DeskForge

+

Welcome to Desk Forge! The app designed for tabletop gamers and hobbyists to effortlessly organise their desk spaces. Whether you're managing your Warhammer armies or juggling multiple creative projects, @@ -12,15 +12,19 @@ const Landing = () => { notes, get AI-powered suggestions, and spend more time doing what you love instead of stressing about organisation!

-
+
- + - +
- +
); }; From f1b12fd3e3801b0e35cf037a3063a3313763d971 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 16:35:58 +0200 Subject: [PATCH 018/176] line height --- frontend/src/pages/Landing.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index 2f13557019..ca42dde7dc 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -4,7 +4,7 @@ const Landing = () => { return (

DeskForge

-

+

Welcome to Desk Forge! The app designed for tabletop gamers and hobbyists to effortlessly organise their desk spaces. Whether you're managing your Warhammer armies or juggling multiple creative projects, From d91d17f56c009c9edfc05c74cfdd35c76a21d329 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 24 Jun 2025 16:48:00 +0200 Subject: [PATCH 019/176] adding a logo --- .gitignore | 3 +++ frontend/src/pages/Landing.jsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3d70248ba2..0e1cb13348 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ node_modules .env.test.local .env.production.local +*.png +*.jpg + build npm-debug.log* diff --git a/frontend/src/pages/Landing.jsx b/frontend/src/pages/Landing.jsx index ca42dde7dc..97d6ce62a9 100644 --- a/frontend/src/pages/Landing.jsx +++ b/frontend/src/pages/Landing.jsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; const Landing = () => { return (

-

DeskForge

+ Logo

Welcome to Desk Forge! The app designed for tabletop gamers and hobbyists to effortlessly organise their desk spaces. Whether you're From 644ce5f92f81b5980fc318eebd870a881d589c9e Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 10:00:42 +0200 Subject: [PATCH 020/176] basic styling for login --- frontend/src/pages/Login.jsx | 72 ++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 07467f1d54..75d44d97a2 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -41,38 +41,48 @@ const Login = () => { }; return ( -

-
-

Log in

-
-
- - setEmail(e.target.value)} - /> -
-
- - setPassword(e.target.value)} - /> -
- - {error &&

{error}

} -
+
+

Log in

+
+
+ + setEmail(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
+
+ + setPassword(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
+ + {error &&

{error}

} +
-

- Don’t have an account? Register here -

- ← Back to Home - {isLoading &&

Logging in...

} -
+

+ Don’t have an account?{" "} + + Register here + +

+ + ← Back to Home + + {isLoading &&

Logging in...

}
); }; From 36a580cf7e5d7cc04799a9d6bdf9a6e9bb539f87 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 10:04:52 +0200 Subject: [PATCH 021/176] basic styling for registration --- frontend/src/pages/Register.jsx | 95 ++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index 8171c55f1d..0c706830d7 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -37,49 +37,60 @@ const Register = () => { }; return ( -
-
-

Create an Account

-
-
- - setName(e.target.value)} - /> -
-
- - setEmail(e.target.value)} - /> -
-
- - setPassword(e.target.value)} - /> -
- - {error &&

{error}

} -
+
+

Register

+
+
+ + setName(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
+
+ + setEmail(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
+
+ + setPassword(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
+ + {error &&

{error}

} +
-

- Already have an account? Log in -

- ← Back to Home -
+

+ Already have an account?{" "} + + Log in + +

+ + ← Back to Home +
); }; From c5b71f443f443c0263ef2d0777827b5406ff0710 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 10:09:22 +0200 Subject: [PATCH 022/176] todo notes --- frontend/src/pages/Login.jsx | 1 + frontend/src/pages/Register.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 75d44d97a2..0e642d90f4 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -1,3 +1,4 @@ +// TODO: add validation // TODO: check if an email doesnt exist as an account, id not, give a message that the accoutn doesnt exist and to register import { useState } from "react"; diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index 0c706830d7..232ebe200d 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -1,3 +1,4 @@ +// TODO: add validation // TODO: check if email already exists, if it does give a message and ask them to log in instead import { useState } from "react"; From c772ba4df75c37ec5fb7c4b54d2682a88f505226 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 12:54:31 +0200 Subject: [PATCH 023/176] adding register and login validation on the frontned --- frontend/src/pages/Login.jsx | 9 +++++++++ frontend/src/pages/Register.jsx | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 0e642d90f4..acb4a9de3d 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -18,6 +18,15 @@ const Login = () => { e.preventDefault(); setIsLoading(true); + if (!email || !password) { + setError("Email and password are required"); + return; + } + + if (!email.includes("@") || !email.includes(".")) { + setError("Please enter a valid email"); + } + try { const response = await fetch(`${apiUrl}/auth/login`, { method: "POST", diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index 232ebe200d..4418d625eb 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -1,4 +1,3 @@ -// TODO: add validation // TODO: check if email already exists, if it does give a message and ask them to log in instead import { useState } from "react"; @@ -10,12 +9,33 @@ const apiUrl = "http://localhost:8080"; const Register = () => { const [name, setName] = useState(""); const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); const [email, setEmail] = useState(""); const [error, setError] = useState(""); const handleRegister = async (e) => { e.preventDefault(); + if (!email || !password || !confirmPassword) { + setError("All fields are required"); + return; + } + + if (!email.includes("@") || !email.includes(".")) { + setError("Please enter a valid email"); + return; + } + + if (password.length < 6) { + setError("Password must be at least 6 characters"); + return; + } + + if (password !== confirmPassword) { + setError("Passwords do not match"); + return; + } + try { const response = await fetch(`${apiUrl}/auth/register`, { method: "POST", @@ -74,6 +94,17 @@ const Register = () => { className="px-2 py-1 border-1 rounded-[5px]" />
+
+ + setConfirmPassword(e.target.value)} + className="px-2 py-1 border-1 rounded-[5px]" + /> +
- {error &&

{error}

}

@@ -92,7 +99,17 @@ const Login = () => { ← Back to Home - {isLoading &&

Logging in...

} +
+ {isLoading && ( +

+ Logging in... +

+ )} +
); }; diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index 4418d625eb..f1568a16e6 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -58,9 +58,18 @@ const Register = () => { }; return ( -
+

Register

+
+

+ {error || "placeholder"} +

+
{ > Register - {error &&

{error}

}

From e4324eed2b4a29dc2a8eb756a51fd7818b7cfd9f Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 14:58:16 +0200 Subject: [PATCH 025/176] secret key issues and authorisation --- backend/models/User.js | 2 +- backend/routes/auth.js | 32 +++++++++++++++++++------------- backend/server.js | 10 ++++++---- frontend/src/pages/Register.jsx | 11 +++++++++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/backend/models/User.js b/backend/models/User.js index b63cc02042..2d9abe2bdd 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -2,7 +2,7 @@ import bcrypt from "bcrypt"; import mongoose from "mongoose"; const userSchema = new mongoose.Schema({ - username: { type: String, required: true, unique: true, minlength: 3 }, + name: { type: String, required: true, minlength: 3 }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, minlength: 6 }, }); diff --git a/backend/routes/auth.js b/backend/routes/auth.js index a06fdb8a59..30ec56901a 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -1,3 +1,6 @@ +import dotenv from "dotenv"; +dotenv.config(); + import express from "express"; import jwt from "jsonwebtoken"; @@ -8,21 +11,25 @@ const JWT_SECRET = process.env.JWT_SECRET; // signing up as a user router.post("/register", async (req, res) => { - const { username, email, password } = req.body; + const { name, email, password } = req.body; try { - const existingUser = await User.findOne({ $or: [{ email }, { username }] }); + const existingUser = await User.findOne({ email }); if (existingUser) { return res .status(400) - .json({ success: false, message: "Username or email already exists." }); + .json({ success: false, message: "Email already exists." }); } - const newUser = new User({ username, email, password }); + const newUser = new User({ name, email, password }); await newUser.save(); + if (!JWT_SECRET) { + console.error("JWT_SECRET is missing or empty!"); + } + const token = jwt.sign( - { id: newUser._id, username: newUser.username }, + { id: newUser._id, user: newUser.email }, JWT_SECRET, { expiresIn: "7d", @@ -31,6 +38,7 @@ router.post("/register", async (req, res) => { res.status(201).json({ success: true, message: "User created", token }); } catch (error) { + console.log(error); res .status(500) .json({ success: false, message: "Error creating user", error }); @@ -56,17 +64,15 @@ router.post("/login", async (req, res) => { .json({ success: false, message: "Invalid email or password" }); } - const token = jwt.sign( - { id: user._id, username: user.username }, - JWT_SECRET, - { - expiresIn: "7d", - } - ); + const token = jwt.sign({ id: user._id, user: user.email }, JWT_SECRET, { + expiresIn: "7d", + }); res.status(200).json({ success: true, message: "Logged in", token }); } catch (error) { - res.status(500).json({ success: false, message: "Login error", error }); + res + .status(500) + .json({ success: false, message: error.message || "Login error", error }); } }); diff --git a/backend/server.js b/backend/server.js index 35674bfde0..ab2a8e7fe2 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,5 +1,7 @@ -import cors from "cors"; import dotenv from "dotenv"; +dotenv.config(); + +import cors from "cors"; import express from "express"; import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; @@ -7,8 +9,6 @@ import mongoose from "mongoose"; import authenticate from "./middlewares/auth.js"; import authRoutes from "./routes/auth.js"; -dotenv.config(); - const port = process.env.PORT || 8080; const app = express(); @@ -22,9 +22,11 @@ app.use(cors()); app.use(express.json()); app.get("/", (req, res) => { - res.send("Hello Technigo!"); + res.send("Hello DeskForge!"); }); +app.use("/auth", authRoutes); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index f1568a16e6..2d5d997676 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -21,6 +21,11 @@ const Register = () => { return; } + if (!name || name.length < 3) { + setError("Name must be at least 3 characters"); + return; + } + if (!email.includes("@") || !email.includes(".")) { setError("Please enter a valid email"); return; @@ -43,8 +48,10 @@ const Register = () => { body: JSON.stringify({ name, email, password }), }); + const data = await response.json(); + if (!response.ok) { - throw new Error("Registration failed"); + throw new Error(data.message || "Registration failed"); } alert("Registration successful!"); @@ -87,7 +94,7 @@ const Register = () => { type="email" id="email" placeholder="you@example.com" - vbalue={email} + value={email} onChange={(e) => setEmail(e.target.value)} className="px-2 py-1 border-1 rounded-[5px]" /> From bddd7918d7c4331e9ca181e089827d22a710a9c3 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 25 Jun 2025 15:03:37 +0200 Subject: [PATCH 026/176] so my dahsboard shows --- frontend/src/pages/Dashboard.jsx | 4 +++- frontend/src/pages/Login.jsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 45b0ea180a..84fe61ac81 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,3 +1,5 @@ -const Dashboard = () => {}; +const Dashboard = () => { + return

This is your dashboard

; +}; export default Dashboard; diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index aabbb75d5c..99e02dffa6 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -41,7 +41,7 @@ const Login = () => { localStorage.setItem("token", data.token); - navigate("/app"); + navigate("/dashboard"); } catch (error) { setError(error.message); } finally { From 1b15cf0adbd46eed9f67c770e31609a87051c713 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Thu, 26 Jun 2025 13:14:37 +0200 Subject: [PATCH 027/176] adding the nav --- frontend/src/App.jsx | 11 +++++++---- frontend/src/components/Layout.jsx | 15 +++++++++++++++ frontend/src/components/Nav.jsx | 15 +++++++++++++++ frontend/src/pages/Dashboard.jsx | 6 +++++- 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/Layout.jsx create mode 100644 frontend/src/components/Nav.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 20eb288dbc..bf1be34cec 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -6,18 +6,21 @@ import Register from "./pages/Register"; import Suggestions from "./pages/Suggestions"; import Upload from "./pages/Upload"; import NotFound from "./pages/NotFound"; +import Layout from "./components/Layout"; export const App = () => { return ( } /> - } /> - } /> } /> - } /> - } /> + } /> } /> + }> + } /> + } /> + } /> + ); diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/Layout.jsx new file mode 100644 index 0000000000..18bf34dc77 --- /dev/null +++ b/frontend/src/components/Layout.jsx @@ -0,0 +1,15 @@ +import { Outlet } from "react-router-dom"; +import Nav from "./Nav"; + +const Layout = () => { + return ( + <> +
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 3fa415c933..9dbe53f58c 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,4 +1,17 @@ +import { useNavigate } from "react-router-dom"; +import { useEffect } from "react"; + const Dashboard = () => { + const navigate = useNavigate(); + + useEffect(() => { + const token = localStorage.getItem("token"); + if (!token) { + navigate("/login"); + return; + } + }, [navigate]); + return (

My dashboard

From 0c2c20939ef39d31d2dd80ab1e537f248f80596d Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 30 Jun 2025 11:42:12 +0200 Subject: [PATCH 032/176] creating and adding a sidebar --- frontend/src/components/SideBar.jsx | 24 ++++++++++++++++++++++++ frontend/src/pages/Dashboard.jsx | 9 +++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/SideBar.jsx diff --git a/frontend/src/components/SideBar.jsx b/frontend/src/components/SideBar.jsx new file mode 100644 index 0000000000..f1fe1569b5 --- /dev/null +++ b/frontend/src/components/SideBar.jsx @@ -0,0 +1,24 @@ +import { Link } from "react-router-dom"; + +const SideBar = () => { + return ( +
+
    + +
  • Dashboard
  • + + +
  • Upload
  • + + +
  • Suggestions
  • + + +
  • Settings
  • + +
+
+ ); +}; + +export default SideBar; diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 9dbe53f58c..6610d6da5d 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,6 +1,8 @@ import { useNavigate } from "react-router-dom"; import { useEffect } from "react"; +import SideBar from "../components/SideBar"; + const Dashboard = () => { const navigate = useNavigate(); @@ -13,8 +15,11 @@ const Dashboard = () => { }, [navigate]); return ( -
-

My dashboard

+
+ +
+

My dashboard

+
); }; From 59449156d012efc10e61ce3e19937b12dc970d6b Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Mon, 30 Jun 2025 12:36:49 +0200 Subject: [PATCH 033/176] organising the structure of my dashboard --- frontend/src/components/Nav.jsx | 2 +- frontend/src/pages/Dashboard.jsx | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Nav.jsx b/frontend/src/components/Nav.jsx index 364799cbe9..e0eacbff69 100644 --- a/frontend/src/components/Nav.jsx +++ b/frontend/src/components/Nav.jsx @@ -45,7 +45,7 @@ const Nav = () => {
Logo -

Welcome {userName || "..."}!

+

Hi, {userName || "..."}!

+ +
+ + + +
+ +

Accepted formats: JPG, PNG, GIF

+

Max file size: 5MB

+ + {message &&

{message}

} + + {/* Preview uploaded image */} + {uploadedUrl && ( +
+ Uploaded preview + +
+ )} +
+ ); +}; export default Upload; From fbc0d200d8b1d58178049faa7e35f8e27b69d720 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 1 Jul 2025 12:52:42 +0200 Subject: [PATCH 036/176] adding the sidebar to the photo upload page --- frontend/src/pages/Upload.jsx | 94 ++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx index e33c3fe87f..1b8f479386 100644 --- a/frontend/src/pages/Upload.jsx +++ b/frontend/src/pages/Upload.jsx @@ -1,4 +1,5 @@ import { useState, useRef } from "react"; +import SideBar from "../components/SideBar"; // change this once on render const apiUrl = "http://localhost:8080"; @@ -69,62 +70,65 @@ const Upload = () => { }; return ( -
-

Upload New Photo

- - - - - -
- +
+ +
+

Upload New Photo

+ + -
- -

Accepted formats: JPG, PNG, GIF

-

Max file size: 5MB

- {message &&

{message}

} +
+ - {/* Preview uploaded image */} - {uploadedUrl && ( -
- Uploaded preview
- )} + +

Accepted formats: JPG, PNG, GIF

+

Max file size: 5MB

+ + {message &&

{message}

} + + {/* Preview uploaded image */} + {uploadedUrl && ( +
+ Uploaded preview + +
+ )} +
); }; From a15d46d7e742c42585aa9c578c8470b632e52822 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 1 Jul 2025 12:55:54 +0200 Subject: [PATCH 037/176] adding the sidebar to the other pages --- frontend/src/App.jsx | 2 ++ frontend/src/pages/Settings.jsx | 15 +++++++++++++++ frontend/src/pages/Suggestions.jsx | 14 +++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/Settings.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index bf1be34cec..5f3257771b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,6 +7,7 @@ import Suggestions from "./pages/Suggestions"; import Upload from "./pages/Upload"; import NotFound from "./pages/NotFound"; import Layout from "./components/Layout"; +import Settings from "./pages/Settings"; export const App = () => { return ( @@ -20,6 +21,7 @@ export const App = () => { } /> } /> } /> + } /> diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx new file mode 100644 index 0000000000..84ea926e89 --- /dev/null +++ b/frontend/src/pages/Settings.jsx @@ -0,0 +1,15 @@ +import SideBar from "../components/SideBar"; + +const Settings = () => { + return ( +
+ +
+

Settings

+ {/* Settings content goes here */} +
+
+ ); +}; + +export default Settings; diff --git a/frontend/src/pages/Suggestions.jsx b/frontend/src/pages/Suggestions.jsx index 2958506505..e8b450e81d 100644 --- a/frontend/src/pages/Suggestions.jsx +++ b/frontend/src/pages/Suggestions.jsx @@ -1,3 +1,15 @@ -const Suggestions = () => {}; +import SideBar from "../components/SideBar"; + +const Suggestions = () => { + return ( +
+ +
+

Suggestions

+ {/* Suggestions content goes here */} +
+
+ ); +}; export default Suggestions; From 6fd657a5cd6aa6564faef7bc60f6c2783ae43b03 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 1 Jul 2025 16:16:11 +0200 Subject: [PATCH 038/176] adding a desk schema --- backend/models/Desk.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 backend/models/Desk.js diff --git a/backend/models/Desk.js b/backend/models/Desk.js new file mode 100644 index 0000000000..91f62f066d --- /dev/null +++ b/backend/models/Desk.js @@ -0,0 +1,22 @@ +import mongoose from "mongoose"; + +const deskSchema = new mongoose.Schema({ + userId: { + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: "User", + }, + imageUrl: { + type: String, + required: true, + }, + createdAt: { + type: Date, + default: Date.now, + }, + suggestions: [{ type: String }], +}); + +const Desk = mongoose.model("Desk", deskSchema); + +export default Desk; From 9388c0023197e70aa8185d561e77a53b3a03826f Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Tue, 1 Jul 2025 16:25:11 +0200 Subject: [PATCH 039/176] saving a new desk to the database --- backend/routes/upload.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/routes/upload.js b/backend/routes/upload.js index 19af2f2961..014a38b304 100644 --- a/backend/routes/upload.js +++ b/backend/routes/upload.js @@ -1,4 +1,5 @@ import express from "express"; +import Desk from "../models/Desk.js"; import multer from "multer"; import authenticate from "../middlewares/auth.js"; import { v2 as cloudinary } from "cloudinary"; @@ -31,11 +32,24 @@ router.post("/", authenticate, upload.single("image"), async (req, res) => { // delete temporary file await fs.unlink(file.path); + // Save to MongoDB Desk collection + const newDesk = new Desk({ + userId: req.user._id, + imageUrl: result.secure_url, + suggestions: [], + }); + + await newDesk.save(); + res.status(200).json({ success: true, - message: "Photo uploaded!", + message: "Photo uploaded and saved to DB!", url: result.secure_url, id: result.public_id, + desk: { + _id: newDesk._id, + suggestions: newDesk.suggestions, + }, }); } catch (error) { console.error(error); From 6eebb674a1f3abb55f2742488dfbd425a200e7e0 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Wed, 2 Jul 2025 14:03:09 +0200 Subject: [PATCH 040/176] showing latest upload on the uploads page --- backend/routes/upload.js | 37 ++++++++++++++++++++++++++++++++++- backend/server.js | 4 ++-- frontend/src/pages/Upload.jsx | 37 ++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/backend/routes/upload.js b/backend/routes/upload.js index 014a38b304..1814074609 100644 --- a/backend/routes/upload.js +++ b/backend/routes/upload.js @@ -34,7 +34,7 @@ router.post("/", authenticate, upload.single("image"), async (req, res) => { // Save to MongoDB Desk collection const newDesk = new Desk({ - userId: req.user._id, + userId: req.user.id, imageUrl: result.secure_url, suggestions: [], }); @@ -57,4 +57,39 @@ router.post("/", authenticate, upload.single("image"), async (req, res) => { } }); +// GET latest desk for logged-in user +router.get("/latest", authenticate, async (req, res) => { + try { + // Find latest desk for the user by createdAt descending + const latestDesk = await Desk.findOne({ userId: req.user._id }) + .sort({ createdAt: -1 }) + .exec(); + + if (!latestDesk) { + return res.status(404).json({ success: false, message: "No desk found" }); + } + + res.status(200).json({ success: true, desk: latestDesk }); + } catch (error) { + console.error(error); + res + .status(500) + .json({ success: false, message: "Failed to fetch latest desk" }); + } +}); + +router.get("/desks", authenticate, async (req, res) => { + try { + // newest desk first + const desks = await Desk.find({ userId: req.user.id }) + .sort({ createdAt: -1 }) + .exec(); + + res.status(200).json({ success: true, desks }); + } catch (error) { + console.error(error); + res.status(500).json({ success: false, message: "Failed to fetch desks" }); + } +}); + export default router; diff --git a/backend/server.js b/backend/server.js index d279c419c8..b83967231e 100644 --- a/backend/server.js +++ b/backend/server.js @@ -8,7 +8,7 @@ import mongoose from "mongoose"; import authenticate from "./middlewares/auth.js"; import authRoutes from "./routes/auth.js"; -import uploadRoute from "./routes/upload.js"; +import uploadRoutes from "./routes/upload.js"; const port = process.env.PORT || 8080; const app = express(); @@ -28,7 +28,7 @@ app.get("/", (req, res) => { app.use("/auth", authRoutes); -app.use("/upload", uploadRoute); +app.use("/upload", uploadRoutes); // Start the server app.listen(port, () => { diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx index 1b8f479386..6701987191 100644 --- a/frontend/src/pages/Upload.jsx +++ b/frontend/src/pages/Upload.jsx @@ -1,4 +1,4 @@ -import { useState, useRef } from "react"; +import { useState, useRef, useEffect } from "react"; import SideBar from "../components/SideBar"; // change this once on render @@ -7,13 +7,36 @@ const apiUrl = "http://localhost:8080"; const Upload = () => { const [file, setFile] = useState(null); const [message, setMessage] = useState(""); - const [uploadedUrl, setUploadedUrl] = useState(""); // store uploaded photo url + const [uploadedUrl, setUploadedUrl] = useState(""); const fileInputRef = useRef(); + useEffect(() => { + const fetchDesks = async () => { + const token = localStorage.getItem("token"); + try { + const response = await fetch(`${apiUrl}/upload/desks`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (!response.ok) throw new Error("Failed to fetch desks"); + + const data = await response.json(); + + if (data.desks && data.desks.length > 0) { + setUploadedUrl(data.desks[0].imageUrl); + } + } catch (error) { + setMessage(error.message); + } + }; + + fetchDesks(); + }, []); + const handleFileChange = (e) => { setFile(e.target.files[0]); setMessage(""); - setUploadedUrl(""); // clear old uploaded url when new file selected }; const handleSelectClick = () => { @@ -51,7 +74,7 @@ const Upload = () => { const data = await response.json(); setMessage("File uploaded successfully!"); - setUploadedUrl(data.url); // show preview with this URL + setUploadedUrl(data.url); } catch (error) { setMessage(error.message); setUploadedUrl(""); @@ -61,7 +84,6 @@ const Upload = () => { const handleCancel = () => { setFile(null); setMessage(""); - setUploadedUrl(""); }; const handleGenerateSuggestions = () => { @@ -72,7 +94,7 @@ const Upload = () => { return (
-
+

Upload New Photo

{ {/* Preview uploaded image */} {uploadedUrl && ( -
+
+

Latest photo

Uploaded preview Date: Wed, 2 Jul 2025 16:32:59 +0200 Subject: [PATCH 041/176] adding delete photo functionality on backend and frontend --- backend/routes/upload.js | 31 ++++++++++++++++++++ frontend/src/pages/Upload.jsx | 54 ++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/backend/routes/upload.js b/backend/routes/upload.js index 1814074609..e72e4121d3 100644 --- a/backend/routes/upload.js +++ b/backend/routes/upload.js @@ -92,4 +92,35 @@ router.get("/desks", authenticate, async (req, res) => { } }); +router.delete("/desks/:id", authenticate, async (req, res) => { + try { + const deskId = req.params.id; + + // Find the desk by ID and ensure it belongs to the user + const desk = await Desk.findOneAndDelete({ + _id: deskId, + userId: req.user.id, + }); + + if (!desk) { + return res + .status(404) + .json({ success: false, message: "Desk not found" }); + } + + // delete the image from Cloudinary + // extract public ID from URL + const publicId = desk.imageUrl.split("/").slice(-2).join("/").split(".")[0]; + + await cloudinary.uploader.destroy(publicId); + + res + .status(200) + .json({ success: true, message: "Desk deleted successfully" }); + } catch (error) { + console.error(error); + res.status(500).json({ success: false, message: "Failed to delete desk" }); + } +}); + export default router; diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx index 6701987191..b6da71e8c0 100644 --- a/frontend/src/pages/Upload.jsx +++ b/frontend/src/pages/Upload.jsx @@ -8,6 +8,7 @@ const Upload = () => { const [file, setFile] = useState(null); const [message, setMessage] = useState(""); const [uploadedUrl, setUploadedUrl] = useState(""); + const [desk, setDesk] = useState(null); const fileInputRef = useRef(); useEffect(() => { @@ -24,6 +25,7 @@ const Upload = () => { const data = await response.json(); if (data.desks && data.desks.length > 0) { + setDesk(data.desks[0]); setUploadedUrl(data.desks[0].imageUrl); } } catch (error) { @@ -91,6 +93,29 @@ const Upload = () => { alert("Suggestions triggered! (not implemented yet)"); }; + // TODO: need a delete route in the backend + const handleDelete = async (deskId) => { + const token = localStorage.getItem("token"); + + try { + const response = await fetch(`${apiUrl}/upload/desks/${deskId}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error("Failed to delete file"); + } + + setMessage("File deleted successfully!"); + setUploadedUrl(""); + } catch (error) { + setMessage(error.message); + } + }; + return (
@@ -132,25 +157,34 @@ const Upload = () => {

Accepted formats: JPG, PNG, GIF

Max file size: 5MB

- {message &&

{message}

} - +

Latest photo

{/* Preview uploaded image */} - {uploadedUrl && ( + {uploadedUrl ? (
-

Latest photo

Uploaded preview - +
+ + +
+ ) : ( +

No photos yet, upload a photo to get started!

)} + {message &&

{message}

}
); From 40ebdf458757bb57f5852520e2346a9118a0a5bd Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Thu, 3 Jul 2025 20:10:56 +0200 Subject: [PATCH 042/176] editing notes and removing unused imports --- backend/routes/upload.js | 2 +- backend/server.js | 2 -- frontend/src/pages/Upload.jsx | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/routes/upload.js b/backend/routes/upload.js index e72e4121d3..f7f0aadc7a 100644 --- a/backend/routes/upload.js +++ b/backend/routes/upload.js @@ -96,7 +96,7 @@ router.delete("/desks/:id", authenticate, async (req, res) => { try { const deskId = req.params.id; - // Find the desk by ID and ensure it belongs to the user + // find desk by ID and check it belongs to the user const desk = await Desk.findOneAndDelete({ _id: deskId, userId: req.user.id, diff --git a/backend/server.js b/backend/server.js index b83967231e..cfddae3016 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,10 +3,8 @@ dotenv.config(); import cors from "cors"; import express from "express"; -import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; -import authenticate from "./middlewares/auth.js"; import authRoutes from "./routes/auth.js"; import uploadRoutes from "./routes/upload.js"; diff --git a/frontend/src/pages/Upload.jsx b/frontend/src/pages/Upload.jsx index b6da71e8c0..1291011610 100644 --- a/frontend/src/pages/Upload.jsx +++ b/frontend/src/pages/Upload.jsx @@ -93,7 +93,6 @@ const Upload = () => { alert("Suggestions triggered! (not implemented yet)"); }; - // TODO: need a delete route in the backend const handleDelete = async (deskId) => { const token = localStorage.getItem("token"); From 6af8e44acded08dbcf5cb66597ae3da6f709d8f7 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Fri, 4 Jul 2025 11:00:50 +0200 Subject: [PATCH 043/176] showing the latest upload preview on my dashboard landing page --- .vscode/settings.json | 3 ++ frontend/src/pages/Dashboard.jsx | 51 +++++++++++++++++++++++++++++--- frontend/src/pages/Upload.jsx | 6 ++-- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..d3cb2ac4db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "postman.settings.dotenv-detection-notification-visibility": false +} diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index f80e27ee58..10506c998d 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,10 +1,16 @@ import { useNavigate } from "react-router-dom"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import SideBar from "../components/SideBar"; +// change this once on render +const apiUrl = "http://localhost:8080"; + const Dashboard = () => { + const [uploadedUrl, setUploadedUrl] = useState(""); + const [desk, setDesk] = useState(null); + const navigate = useNavigate(); useEffect(() => { @@ -15,14 +21,51 @@ const Dashboard = () => { } }, [navigate]); + useEffect(() => { + const fetchDesks = async () => { + const token = localStorage.getItem("token"); + try { + const response = await fetch(`${apiUrl}/upload/desks`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (!response.ok) throw new Error("Failed to fetch desks"); + + const data = await response.json(); + + if (data.desks && data.desks.length > 0) { + setDesk(data.desks[0]); + setUploadedUrl(data.desks[0].imageUrl); + } + } catch (error) { + setMessage(error.message); + } + }; + + fetchDesks(); + }, []); + return (
-

Welcome back!

+

Welcome Back!

+

This is your dashboard!

-

Latest Upload

- +

Latest Upload

+ {/* Preview uploaded image */} + {uploadedUrl ? ( +
+ Uploaded preview +
+ ) : ( +

No photos yet, upload a photo to see it here!

+ )}
{
-

Accepted formats: JPG, PNG, GIF

-

Max file size: 5MB

+

Accepted formats: JPG, PNG, GIF

+

Max file size: 5MB

-

Latest photo

+

Latest Photo

{/* Preview uploaded image */} {uploadedUrl ? (
From 354f561f06a1c9db4758bdd6ed9c686912891f87 Mon Sep 17 00:00:00 2001 From: christina-baldwin Date: Fri, 4 Jul 2025 11:01:34 +0200 Subject: [PATCH 044/176] renaming --- frontend/src/components/SideBar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/SideBar.jsx b/frontend/src/components/SideBar.jsx index f1fe1569b5..ac8176f195 100644 --- a/frontend/src/components/SideBar.jsx +++ b/frontend/src/components/SideBar.jsx @@ -5,7 +5,7 @@ const SideBar = () => {