Adding login auth

This commit is contained in:
hz
2025-05-12 22:24:07 -07:00
parent e2e043070c
commit 0a903bad0b
7 changed files with 299 additions and 86 deletions
+26 -6
View File
@@ -1,17 +1,37 @@
import React from 'react'; import React from "react";
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
import Login from './pages/Login'; import Login from "./pages/Login";
import Register from './pages/Register'; import Register from "./pages/Register";
import { useAuth } from "./AuthContext";
const App = () => { const App = () => {
const { isAuthenticated, logout } = useAuth();
return ( return (
<Router> <div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
{/* Logout button */}
{isAuthenticated && (
<div style={{ display: "flex", justifyContent: "flex-end", padding: "1rem" }}>
<button onClick={logout}>Logout</button>
</div>
)}
{/* Centered content */}
<div
style={{
flex: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Routes> <Routes>
<Route path="/" element={<Login />} /> <Route path="/" element={<Login />} />
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} /> <Route path="/register" element={<Register />} />
</Routes> </Routes>
</Router> </div>
</div>
); );
}; };
+27
View File
@@ -0,0 +1,27 @@
import React, { createContext, useContext, useState } from "react";
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(
!!localStorage.getItem("authToken")
);
const login = (token) => {
localStorage.setItem("authToken", token);
setIsLoggedIn(true);
};
const logout = () => {
localStorage.removeItem("authToken");
setIsLoggedIn(false);
};
return (
<AuthContext.Provider value={{ isLoggedIn, login, logout }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
+39
View File
@@ -0,0 +1,39 @@
import React from "react";
import { useAuth } from "../AuthContext";
import { useNavigate } from "react-router-dom";
const Layout = ({ children }) => {
const { isLoggedIn, logout } = useAuth();
const navigate = useNavigate();
const handleLogout = () => {
logout();
navigate("/login");
};
return (
<div style={{ position: "relative", minHeight: "100vh" }}>
{isLoggedIn && (
<button
onClick={handleLogout}
style={{
position: "absolute",
top: "1rem",
right: "1rem",
padding: "0.5rem 1rem",
borderRadius: "4px",
backgroundColor: "#444",
color: "#fff",
border: "none",
cursor: "pointer",
}}
>
Logout
</button>
)}
{children}
</div>
);
};
export default Layout;
+7 -4
View File
@@ -22,12 +22,13 @@ a:hover {
color: #535bf2; color: #535bf2;
} }
body { /* index.css or global stylesheet */
body, html, #root {
height: 100%;
margin: 0; margin: 0;
display: flex; display: flex;
place-items: center; align-items: center;
min-width: 320px; justify-content: center;
min-height: 100vh;
} }
h1 { h1 {
@@ -66,3 +67,5 @@ button:focus-visible {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
} }
+13 -8
View File
@@ -1,10 +1,15 @@
import { StrictMode } from 'react' import React from "react";
import { createRoot } from 'react-dom/client' import ReactDOM from "react-dom/client";
import './index.css' import App from "./App";
import App from './App.jsx' import { BrowserRouter } from "react-router-dom";
import { AuthProvider } from "./AuthContext";
createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById("root")).render(
<StrictMode> <React.StrictMode>
<BrowserRouter>
<AuthProvider>
<App /> <App />
</StrictMode>, </AuthProvider>
) </BrowserRouter>
</React.StrictMode>
);
+96 -33
View File
@@ -1,65 +1,128 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import config from "../config"; import config from "../config";
import { useAuth } from "../AuthContext";
const styles = {
form: {
backgroundColor: "#2a2a2a",
padding: "2rem",
borderRadius: "8px",
boxShadow: "0 0 10px rgba(0,0,0,0.5)",
width: "100%",
maxWidth: "350px",
display: "flex",
flexDirection: "column",
gap: "1rem",
},
title: {
textAlign: "center",
color: "#f2f2f2",
marginBottom: "1rem",
},
input: {
padding: "0.75rem",
borderRadius: "4px",
border: "1px solid #444",
backgroundColor: "#333",
color: "#eee",
outline: "none",
},
buttonRow: {
display: "flex",
justifyContent: "space-between",
gap: "0.5rem",
},
button: {
flex: 1,
padding: "0.75rem",
borderRadius: "4px",
border: "none",
backgroundColor: "#555",
color: "#fff",
cursor: "pointer",
transition: "background 0.2s ease",
},
};
const Login = () => { const Login = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate(); const navigate = useNavigate();
const { login } = useAuth();
const handleLogin = async (e) => { const [form, setForm] = useState({
e.preventDefault(); username: "",
password: "",
const response = await fetch(`${config.API_BASE_URL}/api/Auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
}); });
if (response.ok) { const handleChange = (e) => {
const data = await response.json(); setForm({ ...form, [e.target.name]: e.target.value });
// Save token etc. };
console.log("Login successful:", data);
// Redirect or update state const handleSubmit = async (e) => {
} else { e.preventDefault();
alert("Login failed.");
const payload = {
username: form.username,
password: form.password,
};
try {
const res = await fetch(`${config.apiBaseUrl}/api/Auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.message || "Login failed");
}
const data = await res.json();
login(data.token); // Save to context/localStorage
navigate("/");
} catch (err) {
console.error("Login error:", err);
alert("Login failed: " + err.message);
} }
}; };
const goToRegister = () => {
navigate("/register");
};
return ( return (
<div> <form onSubmit={handleSubmit} style={styles.form}>
<h2>Login</h2> <h2 style={styles.title}>Login</h2>
<form onSubmit={handleLogin}>
<input <input
type="text" type="text"
name="username"
placeholder="Username" placeholder="Username"
value={username} value={form.username}
onChange={(e) => setUsername(e.target.value)} onChange={handleChange}
style={styles.input}
required required
/> />
<br />
<input <input
type="password" type="password"
name="password"
placeholder="Password" placeholder="Password"
value={password} value={form.password}
onChange={(e) => setPassword(e.target.value)} onChange={handleChange}
style={styles.input}
required required
/> />
<br /> <div style={styles.buttonRow}>
<button type="submit">Login</button> <button type="submit" style={styles.button}>
Login
</button>
<button <button
type="button" type="button"
onClick={goToRegister} onClick={() => navigate("/register")}
style={{ marginLeft: "10px" }} style={styles.button}
> >
Register Register
</button> </button>
</form>
</div> </div>
</form>
); );
}; };
+75 -19
View File
@@ -2,14 +2,56 @@ import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import config from "../config"; import config from "../config";
const styles = {
form: {
backgroundColor: "#2a2a2a",
padding: "2rem",
borderRadius: "8px",
boxShadow: "0 0 10px rgba(0,0,0,0.5)",
width: "100%",
maxWidth: "350px",
display: "flex",
flexDirection: "column",
gap: "1rem",
},
title: {
textAlign: "center",
color: "#f2f2f2",
marginBottom: "1rem",
},
input: {
padding: "0.75rem",
borderRadius: "4px",
border: "1px solid #444",
backgroundColor: "#333",
color: "#eee",
outline: "none",
},
buttonRow: {
display: "flex",
justifyContent: "space-between",
gap: "0.5rem",
},
button: {
flex: 1,
padding: "0.75rem",
borderRadius: "4px",
border: "none",
backgroundColor: "#555",
color: "#fff",
cursor: "pointer",
transition: "background 0.2s ease",
},
};
const Register = () => { const Register = () => {
const navigate = useNavigate();
const [form, setForm] = useState({ const [form, setForm] = useState({
username: "", username: "",
email: "", email: "",
password: "", password: "",
}); });
const [error, setError] = useState("");
const navigate = useNavigate();
const handleChange = (e) => { const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value }); setForm({ ...form, [e.target.name]: e.target.value });
@@ -18,12 +60,6 @@ const Register = () => {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
const payload = {
username: form.username,
password: form.password,
email: form.email,
};
try { try {
const res = await fetch(`${config.apiBaseUrl}/api/Auth/register`, { const res = await fetch(`${config.apiBaseUrl}/api/Auth/register`, {
method: "POST", method: "POST",
@@ -42,7 +78,7 @@ const Register = () => {
throw new Error(data.message || "Registration failed"); throw new Error(data.message || "Registration failed");
} }
alert("Success!"); alert("Registration successful!");
navigate("/login"); navigate("/login");
} catch (err) { } catch (err) {
console.error("Register error:", err); console.error("Register error:", err);
@@ -51,32 +87,52 @@ const Register = () => {
}; };
return ( return (
<div> <div
<h2>Register</h2> style={{
{error && <p style={{ color: "red" }}>{error}</p>} display: "flex",
<form onSubmit={handleSubmit}> justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<form onSubmit={handleSubmit} style={styles.form}>
<h2 style={styles.title}>Register</h2>
<input <input
name="username" style={styles.input}
type="text"
placeholder="Username" placeholder="Username"
name="username"
value={form.username} value={form.username}
onChange={handleChange} onChange={handleChange}
required
/> />
<input <input
name="email" style={styles.input}
type="email"
placeholder="Email" placeholder="Email"
name="email"
value={form.email} value={form.email}
onChange={handleChange} onChange={handleChange}
/> />
<input <input
name="password" style={styles.input}
type="password" type="password"
placeholder="Password" placeholder="Password"
name="password"
value={form.password} value={form.password}
onChange={handleChange} onChange={handleChange}
required
/> />
<button type="submit">Register</button> <div style={styles.buttonRow}>
<button type="submit" style={styles.button}>
Register
</button>
<button
type="button"
style={styles.button}
onClick={() => navigate("/login")}
>
Back
</button>
</div>
</form> </form>
</div> </div>
); );