From 0f9d074d3a9bfc84ae72108a450954a14cf24e44 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 29 Oct 2026 22:41:43 +0100 Subject: [PATCH 1/7] fix: protect profile page from unauthenticated users --- frontend/src/App.tsx | 10 +++++++++- frontend/src/components/ProtectedRoute.tsx | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/ProtectedRoute.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5817264..6f18051 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,7 @@ import CreateAccount from "./pages/CreateAccountPage"; import ProfilePage from "./pages/ProfilePage"; import ArtworkDetailPage from "./pages/ArtworkDetailPage"; import Footer from "./components/Footer"; +import ProtectedRoute from "./components/ProtectedRoute"; function App() { // No footers for Login and CreateAccount Pages @@ -19,7 +20,14 @@ function App() { } /> } /> } /> - } /> + + + + } + /> } /> diff --git a/frontend/src/components/ProtectedRoute.tsx b/frontend/src/components/ProtectedRoute.tsx new file mode 100644 index 0000000..e01f97e --- /dev/null +++ b/frontend/src/components/ProtectedRoute.tsx @@ -0,0 +1,15 @@ +import { Navigate } from "react-router-dom"; + +type ProtectedRouteProps = { + children: React.ReactNode; +}; + +export default function ProtectedRoute({ children }: ProtectedRouteProps) { + const token = sessionStorage.getItem("token"); + + if (!token) { + return ; + } + + return children; +} \ No newline at end of file From 5ef80fa0b044c09a51d771d9559af018a1da0620 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 29 Oct 2026 22:43:48 +0100 Subject: [PATCH 2/7] chore: fix navigation in hero --- frontend/src/components/Hero.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Hero.tsx b/frontend/src/components/Hero.tsx index 8eecfb6..7422ed4 100644 --- a/frontend/src/components/Hero.tsx +++ b/frontend/src/components/Hero.tsx @@ -4,11 +4,13 @@ import { CircleUserRound } from "lucide-react"; import { Link } from "react-router-dom"; function Hero() { + const isLoggedIn = Boolean(sessionStorage.getItem("token")); + return (
From a13ae435d0506e3175a11599f6a6fc78ad93de91 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 29 Oct 2026 22:46:09 +0100 Subject: [PATCH 3/7] chore: change navigation from login to profile page --- frontend/src/pages/LoginPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 247a4ef..dbd8203 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { useState, type SubmitEvent as ReactSubmitEvent } from "react"; import type { User } from "../types/User"; @@ -13,6 +13,8 @@ type LoginResponse = { }; function Login() { + const navigate = useNavigate(); + const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); @@ -59,8 +61,7 @@ function Login() { sessionStorage.setItem("user", JSON.stringify(user)); sessionStorage.setItem("token", token); - setMessage(`Welcome back, ${user.username}.`); - setPassword(""); + navigate("/profile"); } catch (error) { setMessage( error instanceof Error ? error.message : "Something went wrong.", From 45daa0b8a6b695a72556110fa9619091d48e28c1 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 29 Oct 2026 22:50:04 +0100 Subject: [PATCH 4/7] chore: redirect new users to profile after registration --- frontend/src/pages/CreateAccountPage.tsx | 57 ++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/CreateAccountPage.tsx b/frontend/src/pages/CreateAccountPage.tsx index 71069b5..1bc91d7 100644 --- a/frontend/src/pages/CreateAccountPage.tsx +++ b/frontend/src/pages/CreateAccountPage.tsx @@ -1,5 +1,5 @@ import { useState, type SubmitEvent as ReactSubmitEvent } from "react"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import type { User } from "../types/User"; type RegisterResponse = { @@ -7,7 +7,19 @@ type RegisterResponse = { errors?: Array<{ message: string }>; }; +type LoginResponse = { + data?: { + login: { + user: User; + token: string; + }; + }; + errors?: { message: string }[]; +}; + export default function CreateAccount() { + const navigate = useNavigate(); + const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); @@ -46,8 +58,47 @@ export default function CreateAccount() { throw new Error("The server did not return the new account."); } - setMessage(`Account created for ${result.data.register.username}.`); - setPassword(""); + const loginQuery = ` + mutation Login($username: String!, $password: String!) { + login(username: $username, password: $password) { + user { + id + username + } + token + } + } + `; + + const loginResponse = await fetch("/graphql", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: loginQuery, + variables: { username, password }, + }), + }); + + if (!loginResponse.ok) { + throw new Error(`Server error (${loginResponse.status}).`); + } + + const loginResult = (await loginResponse.json()) as LoginResponse; + + if (loginResult.errors?.length) { + throw new Error(loginResult.errors[0].message); + } + + if (!loginResult.data?.login) { + throw new Error("Account was created, but login failed."); + } + + const { user, token } = loginResult.data.login; + + sessionStorage.setItem("user", JSON.stringify(user)); + sessionStorage.setItem("token", token); + + navigate("/profile"); } catch (error) { setMessage( error instanceof Error ? error.message : "Something went wrong.", From d930fcb9e221b7ef614ad97c1d55215ac61446d5 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 29 Oct 2026 23:07:24 +0100 Subject: [PATCH 5/7] feat: add back and logout navigation to profile --- frontend/src/pages/ProfilePage.tsx | 43 +++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index cb75cb2..290db1b 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "react"; import type { Gallery } from "../types/Gallery"; import CreateGalleryModal from "../components/CreateGalleryModal"; import { useGalleryCoverImages } from "../hooks/useGalleryCoverImages"; +import { Link, useNavigate } from "react-router-dom"; const coverColors = [ "#F3CA34", // Yellow @@ -16,6 +17,8 @@ const coverColors = [ ]; export default function ProfilePage() { + const navigate = useNavigate(); + const [coverColor] = useState(() => { const randomIndex = Math.floor(Math.random() * coverColors.length); return coverColors[randomIndex]; @@ -258,8 +261,29 @@ export default function ProfilePage() { reader.readAsDataURL(file); } + function handleLogout() { + sessionStorage.removeItem("user"); + sessionStorage.removeItem("token"); + navigate("/"); + } + return (
+ + ← Back + + + +
+ + +