- Timestamp:
- 02/09/26 21:36:40 (5 months ago)
- Branches:
- master
- Children:
- 8615885
- Parents:
- 140d098
- Location:
- frontend/src
- Files:
-
- 2 added
- 6 edited
-
App.jsx (modified) (1 diff)
-
api/axios.js (modified) (1 diff)
-
main.jsx (modified) (1 diff)
-
pages/Dashboard.jsx (modified) (1 diff)
-
pages/LandingPage/components/Hero.jsx (modified) (2 diffs)
-
pages/LandingPage/components/NavbarLanding.jsx (modified) (1 diff)
-
pages/Login/Login.jsx (added)
-
pages/Register/Register.jsx (added)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/App.jsx
r140d098 r3ebe47c 1 import { Navigate, Route, Routes } from "react-router-dom"; 2 1 3 import LandingPage from "./pages/LandingPage/LandingPage.jsx"; 4 import Login from "./pages/Login/Login.jsx"; 5 import Register from "./pages/Register/Register.jsx"; 6 import Dashboard from "./pages/Dashboard.jsx"; 7 8 function RequireAuth({ children }) { 9 const token = localStorage.getItem("authToken"); 10 if (!token) return <Navigate to="/login" replace />; 11 return children; 12 } 2 13 3 14 function App() { 4 return <LandingPage />; 15 return ( 16 <div data-theme="forest" className="min-h-screen w-full"> 17 <Routes> 18 <Route path="/" element={<LandingPage />} /> 19 <Route path="/login" element={<Login />} /> 20 <Route path="/register" element={<Register />} /> 21 <Route 22 path="/dashboard" 23 element={ 24 <RequireAuth> 25 <Dashboard /> 26 </RequireAuth> 27 } 28 /> 29 <Route path="*" element={<Navigate to="/" replace />} /> 30 </Routes> 31 </div> 32 ); 5 33 } 6 34 -
frontend/src/api/axios.js
r140d098 r3ebe47c 1 1 import axios from "axios"; 2 2 3 export default axios.create({ 4 baseURL: "http://localhost:8080/api", 5 withCredentials: true, // important if using cookies 3 const api = axios.create({ 4 baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api", 6 5 }); 6 7 api.interceptors.request.use((config) => { 8 const token = localStorage.getItem("authToken"); 9 if (token) { 10 config.headers = config.headers ?? {}; 11 config.headers.Authorization = `Bearer ${token}`; 12 } 13 return config; 14 }); 15 16 export default api; -
frontend/src/main.jsx
r140d098 r3ebe47c 1 import { StrictMode } from 'react' 2 import { createRoot } from 'react-dom/client' 3 import './index.css' 4 import App from './App.jsx' 1 import { StrictMode } from "react"; 2 import { createRoot } from "react-dom/client"; 3 import { BrowserRouter } from "react-router-dom"; 5 4 6 createRoot(document.getElementById('root')).render( 5 import "./index.css"; 6 import App from "./App.jsx"; 7 8 createRoot(document.getElementById("root")).render( 7 9 <StrictMode> 8 <App /> 10 <BrowserRouter> 11 <App /> 12 </BrowserRouter> 9 13 </StrictMode>, 10 ) 14 ); -
frontend/src/pages/Dashboard.jsx
r140d098 r3ebe47c 1 // hello world 2 import React from "react"; 1 import React, { useMemo } from "react"; 2 import { Link, useNavigate } from "react-router-dom"; 3 4 const Dashboard = () => { 5 const navigate = useNavigate(); 6 7 const user = useMemo(() => { 8 try { 9 const raw = localStorage.getItem("authUser"); 10 return raw ? JSON.parse(raw) : null; 11 } catch { 12 return null; 13 } 14 }, []); 15 16 const onLogout = () => { 17 localStorage.removeItem("authToken"); 18 localStorage.removeItem("authUser"); 19 navigate("/", { replace: true }); 20 }; 21 22 return ( 23 <div className="min-h-screen p-6"> 24 <div className="max-w-3xl mx-auto"> 25 <div className="flex items-center justify-between gap-3"> 26 <h1 className="text-2xl font-bold">Dashboard</h1> 27 <div className="flex items-center gap-2"> 28 <Link className="btn btn-ghost" to="/"> 29 Landing 30 </Link> 31 <button className="btn btn-outline" onClick={onLogout}> 32 Logout 33 </button> 34 </div> 35 </div> 36 37 <div className="mt-6 card bg-base-200 border border-base-300"> 38 <div className="card-body"> 39 <h2 className="card-title">Account</h2> 40 <div className="text-sm leading-7"> 41 <div> 42 <span className="opacity-70">Username:</span>{" "} 43 <span className="font-medium">{user?.username ?? "—"}</span> 44 </div> 45 <div> 46 <span className="opacity-70">Email:</span>{" "} 47 <span className="font-medium">{user?.email ?? "—"}</span> 48 </div> 49 <div> 50 <span className="opacity-70">User ID:</span>{" "} 51 <span className="font-medium">{user?.userId ?? "—"}</span> 52 </div> 53 </div> 54 </div> 55 </div> 56 </div> 57 </div> 58 ); 59 }; 60 61 export default Dashboard; -
frontend/src/pages/LandingPage/components/Hero.jsx
r140d098 r3ebe47c 1 import React from "react"; 1 import React, { useEffect, useState } from "react"; 2 import { Link } from "react-router-dom"; 2 3 3 4 const Hero = () => { 5 const [isAuthed, setIsAuthed] = useState(() => 6 Boolean(localStorage.getItem("authToken")), 7 ); 8 9 useEffect(() => { 10 const onStorage = (e) => { 11 if (e.key === "authToken") { 12 setIsAuthed(Boolean(e.newValue)); 13 } 14 }; 15 window.addEventListener("storage", onStorage); 16 return () => window.removeEventListener("storage", onStorage); 17 }, []); 18 4 19 return ( 5 20 <div … … 21 36 consistently, and see your progress in one place. 22 37 </p> 23 <button className="btn btn-primary">Get started</button> 38 <Link 39 className="btn btn-primary" 40 to={isAuthed ? "/dashboard" : "/register"} 41 > 42 {isAuthed ? "Go to dashboard" : "Get started"} 43 </Link> 24 44 </div> 25 45 </div> -
frontend/src/pages/LandingPage/components/NavbarLanding.jsx
r140d098 r3ebe47c 1 import React from "react"; 1 import React, { useEffect, useState } from "react"; 2 import { Link, useNavigate } from "react-router-dom"; 2 3 import logo from "../../../assets/logo.png"; 3 4 4 5 const NavbarLanding = () => { 6 const navigate = useNavigate(); 7 const [isAuthed, setIsAuthed] = useState(() => 8 Boolean(localStorage.getItem("authToken")), 9 ); 10 11 useEffect(() => { 12 const onStorage = (e) => { 13 if (e.key === "authToken") { 14 setIsAuthed(Boolean(e.newValue)); 15 } 16 }; 17 window.addEventListener("storage", onStorage); 18 return () => window.removeEventListener("storage", onStorage); 19 }, []); 20 21 const onLogout = () => { 22 localStorage.removeItem("authToken"); 23 localStorage.removeItem("authUser"); 24 setIsAuthed(false); 25 navigate("/", { replace: true }); 26 }; 27 5 28 return ( 6 29 <div className="navbar bg-neutral shadow-sm"> 7 30 <div className="navbar-start"> 8 < a className="btn btn-ghost" aria-label="Trekr home">31 <Link className="btn btn-ghost" aria-label="Trekr home" to="/"> 9 32 <img src={logo} alt="Trekr" className="h-12 w-auto rounded-2xl" /> 10 </ a>33 </Link> 11 34 </div> 12 35 <div className="navbar-end"> 13 <button className="btn bg-black text-green-200 border-black hover:bg-black/90 hover:border-black px-8 mr-6"> 14 Start Tracking 15 </button> 36 {isAuthed ? ( 37 <div className="flex items-center gap-2 mr-6"> 38 <Link className="btn btn-primary" to="/dashboard"> 39 Dashboard 40 </Link> 41 <button className="btn btn-outline" onClick={onLogout}> 42 Logout 43 </button> 44 </div> 45 ) : ( 46 <Link 47 className="btn bg-black text-green-200 border-black hover:bg-black/90 hover:border-black px-8 mr-6" 48 to="/login" 49 > 50 Start Tracking 51 </Link> 52 )} 16 53 </div> 17 54 </div>
Note:
See TracChangeset
for help on using the changeset viewer.
