Changeset 3ebe47c for frontend


Ignore:
Timestamp:
02/09/26 21:36:40 (5 months ago)
Author:
Andrej <asumanovski@…>
Branches:
master
Children:
8615885
Parents:
140d098
Message:

Made authentication on frontend and backend

Location:
frontend/src
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/App.jsx

    r140d098 r3ebe47c  
     1import { Navigate, Route, Routes } from "react-router-dom";
     2
    13import LandingPage from "./pages/LandingPage/LandingPage.jsx";
     4import Login from "./pages/Login/Login.jsx";
     5import Register from "./pages/Register/Register.jsx";
     6import Dashboard from "./pages/Dashboard.jsx";
     7
     8function RequireAuth({ children }) {
     9  const token = localStorage.getItem("authToken");
     10  if (!token) return <Navigate to="/login" replace />;
     11  return children;
     12}
    213
    314function 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  );
    533}
    634
  • frontend/src/api/axios.js

    r140d098 r3ebe47c  
    11import axios from "axios";
    22
    3 export default axios.create({
    4   baseURL: "http://localhost:8080/api",
    5   withCredentials: true, // important if using cookies
     3const api = axios.create({
     4  baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api",
    65});
     6
     7api.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
     16export 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'
     1import { StrictMode } from "react";
     2import { createRoot } from "react-dom/client";
     3import { BrowserRouter } from "react-router-dom";
    54
    6 createRoot(document.getElementById('root')).render(
     5import "./index.css";
     6import App from "./App.jsx";
     7
     8createRoot(document.getElementById("root")).render(
    79  <StrictMode>
    8     <App />
     10    <BrowserRouter>
     11      <App />
     12    </BrowserRouter>
    913  </StrictMode>,
    10 )
     14);
  • frontend/src/pages/Dashboard.jsx

    r140d098 r3ebe47c  
    1 // hello world
    2 import React from "react";
     1import React, { useMemo } from "react";
     2import { Link, useNavigate } from "react-router-dom";
     3
     4const 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
     61export default Dashboard;
  • frontend/src/pages/LandingPage/components/Hero.jsx

    r140d098 r3ebe47c  
    1 import React from "react";
     1import React, { useEffect, useState } from "react";
     2import { Link } from "react-router-dom";
    23
    34const 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
    419  return (
    520    <div
     
    2136            consistently, and see your progress in one place.
    2237          </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>
    2444        </div>
    2545      </div>
  • frontend/src/pages/LandingPage/components/NavbarLanding.jsx

    r140d098 r3ebe47c  
    1 import React from "react";
     1import React, { useEffect, useState } from "react";
     2import { Link, useNavigate } from "react-router-dom";
    23import logo from "../../../assets/logo.png";
    34
    45const 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
    528  return (
    629    <div className="navbar bg-neutral shadow-sm">
    730      <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="/">
    932          <img src={logo} alt="Trekr" className="h-12 w-auto rounded-2xl" />
    10         </a>
     33        </Link>
    1134      </div>
    1235      <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        )}
    1653      </div>
    1754    </div>
Note: See TracChangeset for help on using the changeset viewer.