aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Krönner2020-10-03 12:22:37 +0200
committerGitHub2020-10-03 12:22:37 +0200
commit3985c6f3e179a1d8870eb21176a12b52a9749256 (patch)
tree398977d71b07f67ff44f77a5b62057f51744c938
parent148a9a5a63ed3e73d0a8709e7972fd1b1586c5b8 (diff)
parente5a757786f632ef15aae7f4010e120d5479b869b (diff)
Merge pull request #3 from marvinborner/motto
Motto
-rw-r--r--app.js2
-rw-r--r--auth/index.js2
-rw-r--r--db.js11
-rw-r--r--mottos.txt10
-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
-rw-r--r--tables.sql34
9 files changed, 227 insertions, 6 deletions
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/auth/index.js b/auth/index.js
index 7039cdb..7aeaf77 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/db.js b/db.js
index 58673bc..193daa8 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 mottos (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..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);
+ }
+}
diff --git a/tables.sql b/tables.sql
index 6161b03..e630058 100644
--- a/tables.sql
+++ b/tables.sql
@@ -9,12 +9,14 @@ 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 mottos;
+-- 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 +74,23 @@ 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 mottos(
+ id INTEGER PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ description VARCHAR(255) NOT NULL DEFAULT '',
+
+ 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;