aboutsummaryrefslogtreecommitdiff
path: root/mottovote
diff options
context:
space:
mode:
Diffstat (limited to 'mottovote')
-rw-r--r--mottovote/index.js43
-rw-r--r--mottovote/public/index.html23
-rw-r--r--mottovote/public/script.js53
-rw-r--r--mottovote/public/style.css55
4 files changed, 174 insertions, 0 deletions
diff --git a/mottovote/index.js b/mottovote/index.js
new file mode 100644
index 0000000..df1f6a4
--- /dev/null
+++ b/mottovote/index.js
@@ -0,0 +1,43 @@
+const express = require("express");
+const db = require("../db");
+const { checkUser } = require("../auth");
+const app = express.Router();
+
+
+app.use("/", checkUser, express.static(__dirname + "/public/"));
+
+app.get("/api/list", checkUser, async (req, res) => {
+ const mottos = await db.query("SELECT id, name, description FROM mottos");
+ const votes = await db.query("SELECT motto_id, votes FROM motto_votes WHERE user_id = ?", [req.session.uid]);
+
+ for (const vote of votes) {
+ const mid = mottos.findIndex((motto) => motto.id === vote.motto_id);
+ if (mid) mottos[mid].votes = vote.votes;
+ }
+ res.json(mottos);
+});
+
+app.put("/api/vote", checkUser, async (req, res) => {
+ await db.query("DELETE FROM motto_votes WHERE user_id = ?", [req.session.uid]);
+ try {
+ if (Object.keys(req.body).length > 3) return res.send("error");
+ for (const mid in req.body) {
+ await db.query(
+ "INSERT INTO motto_votes (user_id, motto_id, votes) VALUES (?, ?, ?)",
+ [req.session.uid, mid, req.body[mid]]
+ );
+ }
+ res.send("ok");
+ } catch (e) {
+ console.error(e);
+ res.send("error");
+ }
+});
+
+// Vote result - admin
+app.get("/api/get", checkUser, async (req, res) => {
+ const votes = await db.query("SELECT m.id, m.name, m.description, SUM(votes) votes FROM motto_votes mv RIGHT JOIN mottos m on mv.motto_id = m.id GROUP BY m.id, m.name, m.description ORDER BY SUM(votes) DESC");
+ res.json(votes);
+});
+
+module.exports = app; \ No newline at end of file
diff --git a/mottovote/public/index.html b/mottovote/public/index.html
new file mode 100644
index 0000000..4e64cb8
--- /dev/null
+++ b/mottovote/public/index.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Motto Vote</title>
+ <link rel="stylesheet" href="https://unpkg.com/purecss@2.0.3/build/pure-min.css"
+ integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ" crossorigin="anonymous" />
+ <link rel="stylesheet" href="style.css" type="text/css" media="all" />
+</head>
+
+<body>
+
+ <main>
+ <div id="vote"></div>
+ <button class="pure-button pure-button-primary" id="voteButton">Vote!</button>
+ </main>
+
+ <script src="script.js"></script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/mottovote/public/script.js b/mottovote/public/script.js
new file mode 100644
index 0000000..05ca940
--- /dev/null
+++ b/mottovote/public/script.js
@@ -0,0 +1,53 @@
+const maxVotes = 3;
+get();
+
+async function get() {
+ const resp = await fetch("api/list");
+ const mottos = await resp.json();
+
+ for (const motto of mottos) {
+ const row = document.createElement("div");
+ const id = motto.id;
+
+ for (let i = 0; i < maxVotes; i++) {
+ const cb = document.createElement("input");
+ cb.type = "checkbox";
+ cb.value = id;
+ cb.checked = motto.votes && motto.votes-- > 0
+ row.append(cb);
+ }
+
+ const text = document.createElement("span");
+ text.textContent = `${motto.name} ${motto.description ? "-" : ""} ${motto.description}`
+
+ row.append(text);
+ window.vote.appendChild(row);
+ }
+ addListeners();
+}
+
+function addListeners() {
+ // Only allow 3 votes
+ const boxes = document.querySelectorAll("input[type=checkbox]");
+ boxes.forEach((box) => {
+ box.addEventListener("change", (evt) => {
+ const checkedSiblings = document.querySelectorAll("input[type=checkbox]:checked");
+ if (checkedSiblings.length > maxVotes) evt.target.checked = false;
+ });
+ });
+
+ window.voteButton.addEventListener("click", async () => {
+ const checked = document.querySelectorAll("input[type=checkbox]:checked");
+ if (checked.length > maxVotes) return; // Shouldn't be necessary
+ const req = {};
+ for (const box of checked) req[box.value] = box.value in req ? req[box.value] + 1 : 1; // Amount of votes
+ const resp = await fetch("api/vote", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(req),
+ });
+ const res = await resp.text();
+ if (res === "ok") location.reload();
+ else alert(res);
+ });
+} \ No newline at end of file
diff --git a/mottovote/public/style.css b/mottovote/public/style.css
new file mode 100644
index 0000000..bf4eac7
--- /dev/null
+++ b/mottovote/public/style.css
@@ -0,0 +1,55 @@
+html,
+body {
+ padding: 0;
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ background-color: #eec0c6;
+ background-image: linear-gradient(315deg, #eec0c6 0%, #7ee8fa 74%);
+}
+
+main {
+ display: flex;
+ flex-flow: row wrap;
+ position: absolute;
+ height: 40%;
+ width: 50%;
+ left: 50%;
+ top: 50%;
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+ padding: 20px;
+ border-radius: 10px;
+ background: white;
+}
+
+#voteButton {
+ height: 10%;
+}
+
+#vote {
+ margin-bottom: 20px;
+ overflow-y: scroll;
+ width: 100%;
+}
+
+#vote div {
+ padding: 5px;
+ display: flex;
+ align-items: center;
+}
+
+#vote div input {
+ margin-right: 5px;
+}
+
+button,
+select {
+ width: 100%;
+}
+
+@media only screen and (max-width: 600px) {
+ div {
+ width: calc(100% - 50px);
+ }
+}