Adding page to view currently uploaded members, as well as verifying admin is logged in
This commit is contained in:
137
client/src/pages/AdminLogin.js
Normal file
137
client/src/pages/AdminLogin.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export default function AdminLogin() {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
if (!username || !password) {
|
||||
setError("⚠️ Please enter both username and password.");
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await fetch("/api/admin/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await resp.json();
|
||||
|
||||
if (resp.ok && data.success) {
|
||||
// Store the auth token in localStorage
|
||||
localStorage.setItem("adminToken", data.token);
|
||||
setError("");
|
||||
navigate("/admin-members");
|
||||
} else {
|
||||
setError(`❌ ${data.error || "Login failed"}`);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(`❌ Network error: ${err.message}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
padding: "2rem",
|
||||
maxWidth: "400px",
|
||||
margin: "5rem auto",
|
||||
border: "1px solid #ddd",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
||||
}}
|
||||
>
|
||||
<h2 style={{ textAlign: "center", marginBottom: "2rem" }}>Admin Login</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div style={{ marginBottom: "1rem" }}>
|
||||
<label htmlFor="username">Username:</label>
|
||||
<input
|
||||
id="username"
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "0.5rem",
|
||||
marginTop: "0.5rem",
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "4px",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
placeholder="Enter your username"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "1.5rem" }}>
|
||||
<label htmlFor="password">Password:</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "0.5rem",
|
||||
marginTop: "0.5rem",
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "4px",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
style={{
|
||||
padding: "1rem",
|
||||
marginBottom: "1rem",
|
||||
backgroundColor: "#ffe0e0",
|
||||
border: "1px solid #ff6b6b",
|
||||
borderRadius: "4px",
|
||||
color: "#c92a2a",
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "0.75rem",
|
||||
backgroundColor: isLoading ? "#ccc" : "#007bff",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
cursor: isLoading ? "not-allowed" : "pointer",
|
||||
fontSize: "1rem",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
85
client/src/pages/AdminMembersView.js
Normal file
85
client/src/pages/AdminMembersView.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from 'react-router';
|
||||
import Table from '@mui/material/Table';
|
||||
|
||||
export default function AdminMembersView() {
|
||||
const [year, setYear] = useState("");
|
||||
const [members, setMembers] = useState([]);
|
||||
const [status, setStatus] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isAdmin = () => {
|
||||
return localStorage.getItem('adminToken') !== null;
|
||||
};
|
||||
|
||||
if (!isAdmin()) {
|
||||
navigate('/admin-login');
|
||||
return <div>Redirecting...</div>;
|
||||
}
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/admin/members/view?year=${year}`);
|
||||
const data = await resp.json();
|
||||
|
||||
if (data.success) {
|
||||
setMembers(data.members);
|
||||
} else {
|
||||
setStatus(`❌ Server error: ${data.error}`);
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus(`❌ Network error: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
<h2>View Members</h2>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div style={{ marginBottom: "1rem" }}>
|
||||
<label htmlFor="year">Year:</label>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
id="year"
|
||||
name="year"
|
||||
value={year}
|
||||
onChange={(e) => setYear(e.target.value)}
|
||||
required
|
||||
min="1900"
|
||||
max="2100"
|
||||
style={{ width: "150px", padding: "0.3rem" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" style={{ padding: "0.5rem 1rem" }}>
|
||||
View Members
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{members.length > 0 && (
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{members.map((member, index) => (
|
||||
<tr key={index}>
|
||||
<td>{member.Name}</td>
|
||||
<td>{member.Email}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
|
||||
{status && <p style={{ marginTop: "1rem" }}>{status}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user