Adding login auth
This commit is contained in:
+31
-11
@@ -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" }}>
|
||||||
<Routes>
|
{/* Logout button */}
|
||||||
<Route path="/" element={<Login />} />
|
{isAuthenticated && (
|
||||||
<Route path="/login" element={<Login />} />
|
<div style={{ display: "flex", justifyContent: "flex-end", padding: "1rem" }}>
|
||||||
<Route path="/register" element={<Register />} />
|
<button onClick={logout}>Logout</button>
|
||||||
</Routes>
|
</div>
|
||||||
</Router>
|
)}
|
||||||
|
|
||||||
|
{/* Centered content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Login />} />
|
||||||
|
<Route path="/login" element={<Login />} />
|
||||||
|
<Route path="/register" element={<Register />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+14
-9
@@ -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>
|
||||||
<App />
|
<BrowserRouter>
|
||||||
</StrictMode>,
|
<AuthProvider>
|
||||||
)
|
<App />
|
||||||
|
</AuthProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
|||||||
+106
-43
@@ -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({
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setForm({ ...form, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const response = await fetch(`${config.API_BASE_URL}/api/Auth/login`, {
|
const payload = {
|
||||||
method: "POST",
|
username: form.username,
|
||||||
headers: { "Content-Type": "application/json" },
|
password: form.password,
|
||||||
body: JSON.stringify({ username, password }),
|
};
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
try {
|
||||||
const data = await response.json();
|
const res = await fetch(`${config.apiBaseUrl}/api/Auth/login`, {
|
||||||
// Save token etc.
|
method: "POST",
|
||||||
console.log("Login successful:", data);
|
headers: {
|
||||||
// Redirect or update state
|
"Content-Type": "application/json",
|
||||||
} else {
|
},
|
||||||
alert("Login failed.");
|
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}
|
||||||
required
|
style={styles.input}
|
||||||
/>
|
required
|
||||||
<br />
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Password"
|
name="password"
|
||||||
value={password}
|
placeholder="Password"
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
value={form.password}
|
||||||
required
|
onChange={handleChange}
|
||||||
/>
|
style={styles.input}
|
||||||
<br />
|
required
|
||||||
<button type="submit">Login</button>
|
/>
|
||||||
|
<div style={styles.buttonRow}>
|
||||||
|
<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
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user