From c80656abf09459c8b13c9cdb33c9a8d3a8cee502 Mon Sep 17 00:00:00 2001 From: LarsVomMars Date: Fri, 2 Oct 2020 23:29:06 +0200 Subject: Added basic voting --- app.js | 2 ++ db.js | 11 ++++++++++ mottos.txt | 10 +++++++++ mottovote/index.js | 21 +++++++++++++++++++ mottovote/public/index.html | 15 ++++++++++++++ mottovote/public/script.js | 49 +++++++++++++++++++++++++++++++++++++++++++++ tables.sql | 21 +++++++++++++------ 7 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 mottos.txt create mode 100644 mottovote/index.js create mode 100644 mottovote/public/index.html create mode 100644 mottovote/public/script.js diff --git a/app.js b/app.js index 23e2715..8200713 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,7 @@ const session = require("express-session"); const { auth, checkUser } = require("./auth"); const motto = require("./motto"); +const mottovote = require("./mottovote"); const quotes = require("./quotes"); const poll = require("./poll"); @@ -28,6 +29,7 @@ app.use(express.json()); app.use("/", express.static(__dirname + "/overview/public")); app.use("/motto", checkUser, motto); +app.use("/mottovote", mottovote); app.use("/quotes", checkUser, quotes); app.use("/poll", checkUser, poll); app.use("/auth", auth); diff --git a/db.js b/db.js index 58673bc..26e3d97 100644 --- a/db.js +++ b/db.js @@ -50,6 +50,17 @@ class DB { }); }); + fs.readFile(__dirname + "/mottos.txt", "utf8", (err, data) => { + if (err) throw err; + + const mottos = data.split("\n"); + mottos.forEach(async (motto) => { + const [name, desc] = motto.split(" - "); + if (motto) + await this.query("INSERT INTO motto_votes (name, description) VALUES (?, ?)", [name, desc]); + }); + }); + const classes = data.split("--"); const userPasswords = {}; console.log("Generating users"); diff --git a/mottos.txt b/mottos.txt new file mode 100644 index 0000000..d0519da --- /dev/null +++ b/mottos.txt @@ -0,0 +1,10 @@ +ABIsexuell - Offen für alles +ABItamin - Der Stoff kam aus dem Lehrerzimmer +KohlrABI - Wir machen uns vom Acker +Suit up! It's gonnABI legendary! - +Westminster ABI - Der Adel geht +WubbalABIdubdub - { Rick and Morty } +cannABIs - Mit einer Tüte fing alles an +kABItalismus - 13 Jahre Klassenkampf +kABItän blaubär - Immer blau und trotzdem schlau +kokABIn - Wir haben die Nase voll diff --git a/mottovote/index.js b/mottovote/index.js new file mode 100644 index 0000000..4806e55 --- /dev/null +++ b/mottovote/index.js @@ -0,0 +1,21 @@ +const express = require("express"); +const db = require("../db"); +const { checkUser } = require("../auth"); +const app = express.Router(); + + +app.use("/", express.static(__dirname + "/public/")); + +app.get("/api/list", async (req, res) => { + const mottos = await db.query("SELECT id, name, description FROM motto_votes ORDER BY name, description"); + res.json(mottos); +}); + +app.put("/api/vote", async (req, res) => { + for (const mid in req.body) { + await db.query("UPDATE motto_votes SET votes = votes + ? WHERE id = ?", [req.body[mid], mid]); + } + res.send("ok"); +}); + +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..27ea837 --- /dev/null +++ b/mottovote/public/index.html @@ -0,0 +1,15 @@ + + + + + + Motto Vote + + + +
+ + + + + \ No newline at end of file diff --git a/mottovote/public/script.js b/mottovote/public/script.js new file mode 100644 index 0000000..7216af4 --- /dev/null +++ b/mottovote/public/script.js @@ -0,0 +1,49 @@ +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; + + const cb = document.createElement("input"); + cb.type = "checkbox"; + cb.id = "motto" + id; + cb.name = id; + + const label = document.createElement("label"); + label.for = "motto" + id; + label.textContent = `${motto.name} ${motto.description ? "-" : ""} ${motto.description}` + + row.append(cb, label); + 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 > 3) evt.target.checked = false; + }); + }); + + window.voteButton.addEventListener("click", async () => { + const checked = document.querySelectorAll("input[type=checkbox]:checked"); + if (checked.length !== 3) return; + const req = {}; + for (const box of checked) req[box.name] = 1; // Amount of votes + const resp = await fetch("api/vote", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(req), + }); + console.log(await resp.text()); + + }); +} \ No newline at end of file diff --git a/tables.sql b/tables.sql index 6161b03..ea86339 100644 --- a/tables.sql +++ b/tables.sql @@ -9,12 +9,13 @@ CREATE TABLE IF NOT EXISTS theme( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- TODO: Remove dropping -DROP TABLE IF EXISTS quotes; -DROP TABLE IF EXISTS ranking_questions; -DROP TABLE IF EXISTS ranking_answers; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS types; -DROP TABLE IF EXISTS class; +-- DROP TABLE IF EXISTS motto_votes; +-- DROP TABLE IF EXISTS quotes; +-- DROP TABLE IF EXISTS ranking_questions; +-- DROP TABLE IF EXISTS ranking_answers; +-- DROP TABLE IF EXISTS users; +-- DROP TABLE IF EXISTS types; +-- DROP TABLE IF EXISTS class; CREATE TABLE IF NOT EXISTS types( id INTEGER PRIMARY KEY AUTO_INCREMENT, @@ -72,3 +73,11 @@ CREATE TABLE IF NOT EXISTS ranking_answers( CONSTRAINT `fk_user_answer1` FOREIGN KEY (user_id) REFERENCES users (id), CONSTRAINT `fk_user_answer2` FOREIGN KEY (answer_id) REFERENCES users (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE IF NOT EXISTS motto_votes( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + description VARCHAR(255) NOT NULL DEFAULT '', + votes INTEGER DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- cgit v1.2.3 From 467eb283888328b977871b6899bede744cedfdd8 Mon Sep 17 00:00:00 2001 From: LarsVomMars Date: Sat, 3 Oct 2020 00:41:10 +0200 Subject: Multivote --- db.js | 2 +- mottovote/index.js | 23 ++++++++++++++++------- mottovote/public/script.js | 30 ++++++++++++++++-------------- tables.sql | 17 +++++++++++++++-- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/db.js b/db.js index 26e3d97..193daa8 100644 --- a/db.js +++ b/db.js @@ -57,7 +57,7 @@ class DB { mottos.forEach(async (motto) => { const [name, desc] = motto.split(" - "); if (motto) - await this.query("INSERT INTO motto_votes (name, description) VALUES (?, ?)", [name, desc]); + await this.query("INSERT INTO mottos (name, description) VALUES (?, ?)", [name, desc]); }); }); diff --git a/mottovote/index.js b/mottovote/index.js index 4806e55..e06f23c 100644 --- a/mottovote/index.js +++ b/mottovote/index.js @@ -4,18 +4,27 @@ const { checkUser } = require("../auth"); const app = express.Router(); -app.use("/", express.static(__dirname + "/public/")); +app.use("/", checkUser, express.static(__dirname + "/public/")); -app.get("/api/list", async (req, res) => { - const mottos = await db.query("SELECT id, name, description FROM motto_votes ORDER BY name, description"); +app.get("/api/list", checkUser, async (req, res) => { + const mottos = await db.query("SELECT id, name, description FROM mottos ORDER BY name, description"); res.json(mottos); }); -app.put("/api/vote", async (req, res) => { - for (const mid in req.body) { - await db.query("UPDATE motto_votes SET votes = votes + ? WHERE id = ?", [req.body[mid], mid]); +app.put("/api/vote", checkUser, async (req, res) => { + await db.query("DELETE FROM motto_votes WHERE user_id = ?", [req.session.uid]); + try { + 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"); } - res.send("ok"); }); module.exports = app; \ No newline at end of file diff --git a/mottovote/public/script.js b/mottovote/public/script.js index 7216af4..9fada2e 100644 --- a/mottovote/public/script.js +++ b/mottovote/public/script.js @@ -1,3 +1,4 @@ +const maxVotes = 3; get(); async function get() { @@ -7,17 +8,18 @@ async function get() { for (const motto of mottos) { const row = document.createElement("div"); const id = motto.id; - - const cb = document.createElement("input"); - cb.type = "checkbox"; - cb.id = "motto" + id; - cb.name = id; - - const label = document.createElement("label"); - label.for = "motto" + id; - label.textContent = `${motto.name} ${motto.description ? "-" : ""} ${motto.description}` - - row.append(cb, label); + + for (let i = 0; i < maxVotes; i++) { + const cb = document.createElement("input"); + cb.type = "checkbox"; + cb.name = id; + row.append(cb); + } + + const text = document.createElement("span"); + text.textContent = `${motto.name} ${motto.description ? "-" : ""} ${motto.description}` + + row.append(text); window.vote.appendChild(row); } addListeners(); @@ -29,15 +31,15 @@ function addListeners() { boxes.forEach((box) => { box.addEventListener("change", (evt) => { const checkedSiblings = document.querySelectorAll("input[type=checkbox]:checked"); - if (checkedSiblings.length > 3) evt.target.checked = false; + if (checkedSiblings.length > maxVotes) evt.target.checked = false; }); }); window.voteButton.addEventListener("click", async () => { const checked = document.querySelectorAll("input[type=checkbox]:checked"); - if (checked.length !== 3) return; + if (checked.length > maxVotes) return; // Shouldn't be necessary const req = {}; - for (const box of checked) req[box.name] = 1; // Amount of votes + for (const box of checked) req[box.name] = box.name in req ? req[box.name] + 1 : 1; // Amount of votes const resp = await fetch("api/vote", { method: "PUT", headers: { "Content-Type": "application/json" }, diff --git a/tables.sql b/tables.sql index ea86339..e630058 100644 --- a/tables.sql +++ b/tables.sql @@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS theme( -- TODO: Remove dropping -- DROP TABLE IF EXISTS motto_votes; +-- DROP TABLE IF EXISTS mottos; -- DROP TABLE IF EXISTS quotes; -- DROP TABLE IF EXISTS ranking_questions; -- DROP TABLE IF EXISTS ranking_answers; @@ -75,9 +76,21 @@ CREATE TABLE IF NOT EXISTS ranking_answers( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE TABLE IF NOT EXISTS motto_votes( +CREATE TABLE IF NOT EXISTS mottos( id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL DEFAULT '', - votes INTEGER DEFAULT 0 + + UNIQUE KEY main (name, description) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS motto_votes( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + motto_id INTEGER NOT NULL, + votes SMALLINT UNSIGNED NOT NULL DEFAULT 0, + + UNIQUE KEY uk_vote (user_id, motto_id), + CONSTRAINT `fk_voted_user` FOREIGN KEY (user_id) REFERENCES users (id), + CONSTRAINT `fk_voted_vote` FOREIGN KEY (motto_id) REFERENCES mottos (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- cgit v1.2.3 From 7639f176abe404cad1a3c9a3b8e2b4918b86087e Mon Sep 17 00:00:00 2001 From: LarsVomMars Date: Sat, 3 Oct 2020 11:31:04 +0200 Subject: Store/change selected votes --- auth/index.js | 2 ++ mottovote/index.js | 8 +++++++- mottovote/public/script.js | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/auth/index.js b/auth/index.js index 0891fc5..0bcbdf5 100644 --- a/auth/index.js +++ b/auth/index.js @@ -37,6 +37,8 @@ app.post("/api/login", async (req, res) => { res.redirect("/auth"); }); +app.use("/api/logout", (req, res) => req.session.destroy() & res.redirect("/")); + app.put("/api/password", checkUser, async (req, res) => { const { pwd, newPwd } = req.body; if (!(pwd && newPwd)) return res.redirect("/auth"); diff --git a/mottovote/index.js b/mottovote/index.js index e06f23c..a485a8b 100644 --- a/mottovote/index.js +++ b/mottovote/index.js @@ -7,7 +7,13 @@ 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 ORDER BY name, description"); + 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); }); diff --git a/mottovote/public/script.js b/mottovote/public/script.js index 9fada2e..338b26b 100644 --- a/mottovote/public/script.js +++ b/mottovote/public/script.js @@ -13,6 +13,7 @@ async function get() { const cb = document.createElement("input"); cb.type = "checkbox"; cb.name = id; + cb.checked = motto.votes && motto.votes-- > 0 row.append(cb); } -- cgit v1.2.3 From 30dd80aa4e7310c3f7958f2db261233583ded909 Mon Sep 17 00:00:00 2001 From: LarsVomMars Date: Sat, 3 Oct 2020 12:14:33 +0200 Subject: Style --- mottovote/index.js | 7 ++++++ mottovote/public/index.html | 12 ++++++++-- mottovote/public/script.js | 4 ++-- mottovote/public/style.css | 55 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 mottovote/public/style.css diff --git a/mottovote/index.js b/mottovote/index.js index a485a8b..d22ba29 100644 --- a/mottovote/index.js +++ b/mottovote/index.js @@ -20,6 +20,7 @@ app.get("/api/list", checkUser, async (req, res) => { app.put("/api/vote", checkUser, async (req, res) => { await db.query("DELETE FROM motto_votes WHERE user_id = ?", [req.session.uid]); try { + if (req.body.entries().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 (?, ?, ?)", @@ -33,4 +34,10 @@ app.put("/api/vote", checkUser, async (req, res) => { } }); +// 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 index 27ea837..4e64cb8 100644 --- a/mottovote/public/index.html +++ b/mottovote/public/index.html @@ -1,15 +1,23 @@ + Motto Vote + + + -
- +
+
+ +
+ \ No newline at end of file diff --git a/mottovote/public/script.js b/mottovote/public/script.js index 338b26b..c8432c5 100644 --- a/mottovote/public/script.js +++ b/mottovote/public/script.js @@ -12,7 +12,7 @@ async function get() { for (let i = 0; i < maxVotes; i++) { const cb = document.createElement("input"); cb.type = "checkbox"; - cb.name = id; + cb.value = id; cb.checked = motto.votes && motto.votes-- > 0 row.append(cb); } @@ -40,7 +40,7 @@ function addListeners() { 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.name] = box.name in req ? req[box.name] + 1 : 1; // Amount of votes + 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" }, 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); + } +} -- cgit v1.2.3 From e5a757786f632ef15aae7f4010e120d5479b869b Mon Sep 17 00:00:00 2001 From: LarsVomMars Date: Sat, 3 Oct 2020 12:19:18 +0200 Subject: Fixes --- mottovote/index.js | 2 +- mottovote/public/script.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mottovote/index.js b/mottovote/index.js index d22ba29..df1f6a4 100644 --- a/mottovote/index.js +++ b/mottovote/index.js @@ -20,7 +20,7 @@ app.get("/api/list", checkUser, async (req, res) => { app.put("/api/vote", checkUser, async (req, res) => { await db.query("DELETE FROM motto_votes WHERE user_id = ?", [req.session.uid]); try { - if (req.body.entries().length > 3) return res.send("error"); + 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 (?, ?, ?)", diff --git a/mottovote/public/script.js b/mottovote/public/script.js index c8432c5..05ca940 100644 --- a/mottovote/public/script.js +++ b/mottovote/public/script.js @@ -46,7 +46,8 @@ function addListeners() { headers: { "Content-Type": "application/json" }, body: JSON.stringify(req), }); - console.log(await resp.text()); - + const res = await resp.text(); + if (res === "ok") location.reload(); + else alert(res); }); } \ No newline at end of file -- cgit v1.2.3