Adding page to create votes and view the votes, rearanging some methods
This commit is contained in:
31
client/package-lock.json
generated
31
client/package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.7",
|
||||
"@mui/material": "^7.3.7",
|
||||
"axios": "^1.13.2",
|
||||
"cra-template-pwa": "2.0.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
@@ -4763,6 +4764,31 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios/node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||
@@ -12713,6 +12739,11 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.7",
|
||||
"@mui/material": "^7.3.7",
|
||||
"axios": "^1.13.2",
|
||||
"cra-template-pwa": "2.0.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
|
||||
@@ -36,3 +36,38 @@
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropbtn {
|
||||
background-color: #f5f5f5;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
display: none; /* Hidden by default */
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.dropdown-content a {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Add zebra striping to tables */
|
||||
table tr:nth-child(odd) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
@@ -4,22 +4,42 @@ import Home from "./pages/Home";
|
||||
import AdminLogin from "./pages/AdminLogin";
|
||||
import AdminMembers from "./pages/AdminMembers";
|
||||
import AdminMembersView from "./pages/AdminMembersView";
|
||||
import AdminCreateVote from "./pages/AdminCreateVote";
|
||||
import PollList from "./pages/PollList";
|
||||
import './App.css';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<nav style={{ padding: "1rem", background: "#f5f5f5" }}>
|
||||
<Link to="/">Home</Link> |
|
||||
<Link to="/admin-login">Admin Login</Link> |
|
||||
<Link to="/admin-members">Admin Members</Link> |
|
||||
<Link to="/admin-members-view">View Members</Link>
|
||||
<Link to="/">Home</Link> |
|
||||
<Link to="/admin-login">Admin Login</Link> |
|
||||
{/* Member dropdown */}
|
||||
<div className="dropdown">
|
||||
<button className="dropbtn">Member ▼</button>
|
||||
<div className="dropdown-content">
|
||||
<a href="/admin-members">Upload Members</a>
|
||||
<a href="/admin-members-view">View Members</a>
|
||||
</div>
|
||||
</div> |
|
||||
<div className="dropdown">
|
||||
<button className="dropbtn">Vote ▼</button>
|
||||
<div className="dropdown-content">
|
||||
<a href="/create-vote">Create Vote</a>
|
||||
<a href="/polls">Poll List</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/admin-login" element={<AdminLogin />} />
|
||||
{/* Member routes */}
|
||||
<Route path="/admin-members" element={<AdminMembers />} />
|
||||
<Route path="/admin-members-view" element={<AdminMembersView />} />
|
||||
{/* Vote routes */}
|
||||
<Route path="/create-vote" element={<AdminCreateVote />} />
|
||||
<Route path="/polls" element={<PollList />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
67
client/src/pages/AdminCreateVote.js
Normal file
67
client/src/pages/AdminCreateVote.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function AdminCreateVote() {
|
||||
const [question, setQuestion] = useState('');
|
||||
const [expiresInHours, setExpiresInHours] = useState('');
|
||||
const [status, setStatus] = useState("");
|
||||
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("question", question);
|
||||
formData.append("expiresInHours", expiresInHours);
|
||||
|
||||
try {
|
||||
const resp = await fetch("/api/admin/new-vote", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await resp.json();
|
||||
|
||||
if (data.success) {
|
||||
setStatus(`✅ Vote created with ID`);
|
||||
} else {
|
||||
setStatus(`❌ Server error: ${data.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus("❌ Failed to create vote. Please try again: " + (error.response?.data?.error || error.message));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Create New Vote</h1>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label>Question:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={question}
|
||||
onChange={(e) => setQuestion(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Expires In (hours):</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
value={expiresInHours}
|
||||
onChange={(e) => setExpiresInHours(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Create Vote</button>
|
||||
</form>
|
||||
|
||||
{status && <p style={{ marginTop: "1rem" }}>{status}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminCreateVote;
|
||||
46
client/src/pages/PollList.js
Normal file
46
client/src/pages/PollList.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import Table from '@mui/material/Table';
|
||||
|
||||
export default function PollList() {
|
||||
const [polls, setPolls] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPolls();
|
||||
}, []);
|
||||
|
||||
const fetchPolls = async () => {
|
||||
try {
|
||||
const response = await axios.post('/api/admin/view-votes');
|
||||
setPolls(response.data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching polls:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Poll List</h1>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Created At</th>
|
||||
<th>Question</th>
|
||||
<th>Member Yes Votes</th>
|
||||
<th>Member No Votes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{polls.map((poll) => (
|
||||
<tr key={poll.id}>
|
||||
<td>{new Date(poll.created_at).toLocaleString()}</td>
|
||||
<td>{poll.question}</td>
|
||||
<td>{poll.member_yes}</td>
|
||||
<td>{poll.member_no}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user