From 72f5731adeebf8d76c5c2dcc266f600ba57812d8 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 10 Oct 2020 17:05:27 +0200 Subject: Added basic admin interface --- admin/index.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ admin/public/index.html | 32 +++++++++++++++++++++++++++++ admin/public/script.js | 22 ++++++++++++++++++++ admin/public/style.css | 33 ++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 admin/index.js create mode 100644 admin/public/index.html create mode 100644 admin/public/script.js create mode 100644 admin/public/style.css (limited to 'admin') diff --git a/admin/index.js b/admin/index.js new file mode 100644 index 0000000..4cea14d --- /dev/null +++ b/admin/index.js @@ -0,0 +1,53 @@ +const express = require("express"); +const db = require("../db"); +const app = express.Router(); +const { checkUser, checkAdmin } = require("../auth"); + +app.use("/", checkAdmin, express.static(__dirname + "/public")); + +// For debugging ig +app.get("/api/all", checkAdmin, async (req, res) => { + const all = []; + + const types = await db.query("SELECT * FROM types ORDER BY id"); + const clazz = await db.query("SELECT * FROM class ORDER BY id"); + const users = await db.query("SELECT * FROM users ORDER BY id"); + const quotes = await db.query("SELECT * FROM quotes ORDER BY id"); + const ranking_questions = await db.query("SELECT * FROM ranking_questions ORDER BY id"); + const ranking_answers = await db.query("SELECT * FROM ranking_answers ORDER BY id"); + const mottos = await db.query("SELECT * FROM mottos ORDER BY id"); + const motto_votes = await db.query("SELECT * FROM motto_votes ORDER BY id"); + + all.push( + { quotes }, + { clazz }, + { users }, + { quotes }, + { ranking_questions }, + { ranking_answers }, + { mottos }, + { motto_votes }, + ); + res.json(all); +}); + +app.get("/api/questions", checkAdmin, async (req, res) => { + const questions = await db.query("SELECT q.id, question, t.name type FROM ranking_questions q INNER JOIN types t on type_id = t.id ORDER BY q.id"); + res.json(questions); +}); + +app.get("/api/answers", checkAdmin, async (req, res) => { + const answers = await db.query( + "SELECT question_id, name, middlename, surname, count(*) count FROM ranking_questions q INNER JOIN ranking_answers a ON q.id = a.question_id INNER JOIN users u ON answer_id = u.id GROUP BY question_id, answer_id ORDER BY count DESC" + ); + res.json(answers); +}); + +app.get("/api/votes", checkAdmin, 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; diff --git a/admin/public/index.html b/admin/public/index.html new file mode 100644 index 0000000..cf5d286 --- /dev/null +++ b/admin/public/index.html @@ -0,0 +1,32 @@ + + + + + + + + + Home + + +
+ Home + Logout +
+ + +
+ Welche/r Schüler/in... + + Welche/r Lehrer/in... + +
+ + + + diff --git a/admin/public/script.js b/admin/public/script.js new file mode 100644 index 0000000..ff6fa2b --- /dev/null +++ b/admin/public/script.js @@ -0,0 +1,22 @@ +fetch("api/questions").then(questions => questions.json()).then(questions => { + fetch("api/answers").then(answers => answers.json()).then(answers => { + questions.forEach(question => question.answers = []); + answers.forEach(answer => questions[answer.question_id - 1].answers.push(answer)); + render(questions); + }); +}); + +function render(questions) +{ + console.log(questions); + const teacher = document.querySelector("ul#teacher"); + const pupil = document.querySelector("ul#pupil"); + questions.forEach(question => { + const list = question.type === "teacher" ? teacher : pupil; + let answers = ""; + question.answers.forEach(answer => { + answers += `
  • ${answer.name} ${answer.middlename ? answer.middlename + " " : ""}${answer.surname}: ${answer.count}
  • ` + }); + list.insertAdjacentHTML("beforeend", `
  • ${question.question}
      ${answers}
  • `); + }); +} diff --git a/admin/public/style.css b/admin/public/style.css new file mode 100644 index 0000000..77853bf --- /dev/null +++ b/admin/public/style.css @@ -0,0 +1,33 @@ +html, +body { + padding: 0; + margin: 0; + height: 100%; + width: 100%; + background-color: #eec0c6; + background-image: linear-gradient(315deg, #eec0c6 0%, #7ee8fa 74%); +} + +.card { + position: absolute; + max-height: 80%; + overflow: auto; + width: 30%; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + padding: 20px; + border-radius: 10px; + background: white; +} + +div { + background: white; +} + +@media only screen and (max-width: 600px) { + .card { + width: calc(100% - 50px); + } +} -- cgit v1.2.3 From b2d9cf884f2aff3445d6619939186e48a683e5a9 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 10 Oct 2020 17:10:01 +0200 Subject: Welp, formatting :D --- admin/index.js | 6 ++++-- admin/public/index.html | 14 +++++++------- admin/public/script.js | 45 +++++++++++++++++++++++++-------------------- 3 files changed, 36 insertions(+), 29 deletions(-) (limited to 'admin') diff --git a/admin/index.js b/admin/index.js index 4cea14d..407bbcf 100644 --- a/admin/index.js +++ b/admin/index.js @@ -32,13 +32,15 @@ app.get("/api/all", checkAdmin, async (req, res) => { }); app.get("/api/questions", checkAdmin, async (req, res) => { - const questions = await db.query("SELECT q.id, question, t.name type FROM ranking_questions q INNER JOIN types t on type_id = t.id ORDER BY q.id"); + const questions = await db.query( + "SELECT q.id, question, t.name type FROM ranking_questions q INNER JOIN types t on type_id = t.id ORDER BY q.id", + ); res.json(questions); }); app.get("/api/answers", checkAdmin, async (req, res) => { const answers = await db.query( - "SELECT question_id, name, middlename, surname, count(*) count FROM ranking_questions q INNER JOIN ranking_answers a ON q.id = a.question_id INNER JOIN users u ON answer_id = u.id GROUP BY question_id, answer_id ORDER BY count DESC" + "SELECT question_id, name, middlename, surname, count(*) count FROM ranking_questions q INNER JOIN ranking_answers a ON q.id = a.question_id INNER JOIN users u ON answer_id = u.id GROUP BY question_id, answer_id ORDER BY count DESC", ); res.json(answers); }); diff --git a/admin/public/index.html b/admin/public/index.html index cf5d286..a611291 100644 --- a/admin/public/index.html +++ b/admin/public/index.html @@ -19,14 +19,14 @@ Logout - +
    - Welche/r Schüler/in... - - Welche/r Lehrer/in... - -
    + Welche/r Schüler/in... + + Welche/r Lehrer/in... + + - + diff --git a/admin/public/script.js b/admin/public/script.js index ff6fa2b..d9fd30e 100644 --- a/admin/public/script.js +++ b/admin/public/script.js @@ -1,22 +1,27 @@ -fetch("api/questions").then(questions => questions.json()).then(questions => { - fetch("api/answers").then(answers => answers.json()).then(answers => { - questions.forEach(question => question.answers = []); - answers.forEach(answer => questions[answer.question_id - 1].answers.push(answer)); - render(questions); - }); -}); +fetch("api/questions") + .then((questions) => questions.json()) + .then((questions) => { + fetch("api/answers") + .then((answers) => answers.json()) + .then((answers) => { + questions.forEach((question) => (question.answers = [])); + answers.forEach((answer) => questions[answer.question_id - 1].answers.push(answer)); + render(questions); + }); + }); -function render(questions) -{ - console.log(questions); - const teacher = document.querySelector("ul#teacher"); - const pupil = document.querySelector("ul#pupil"); - questions.forEach(question => { - const list = question.type === "teacher" ? teacher : pupil; - let answers = ""; - question.answers.forEach(answer => { - answers += `
  • ${answer.name} ${answer.middlename ? answer.middlename + " " : ""}${answer.surname}: ${answer.count}
  • ` - }); - list.insertAdjacentHTML("beforeend", `
  • ${question.question}
      ${answers}
  • `); - }); +function render(questions) { + console.log(questions); + const teacher = document.querySelector("ul#teacher"); + const pupil = document.querySelector("ul#pupil"); + questions.forEach((question) => { + const list = question.type === "teacher" ? teacher : pupil; + let answers = ""; + question.answers.forEach((answer) => { + answers += `
  • ${answer.name} ${answer.middlename ? answer.middlename + " " : ""}${answer.surname}: ${ + answer.count + }
  • `; + }); + list.insertAdjacentHTML("beforeend", `
  • ${question.question}
      ${answers}
  • `); + }); } -- cgit v1.2.3 From 897b4173c5cc190805a96aafe5f9258610332e39 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 10 Oct 2020 17:49:51 +0200 Subject: Added vote stats --- admin/public/index.html | 15 ++++++--------- admin/public/ranking.html | 32 ++++++++++++++++++++++++++++++++ admin/public/ranking.js | 26 ++++++++++++++++++++++++++ admin/public/script.js | 27 --------------------------- admin/public/style.css | 4 ++-- admin/public/votes.html | 28 ++++++++++++++++++++++++++++ admin/public/votes.js | 37 +++++++++++++++++++++++++++++++++++++ 7 files changed, 131 insertions(+), 38 deletions(-) create mode 100644 admin/public/ranking.html create mode 100644 admin/public/ranking.js delete mode 100644 admin/public/script.js create mode 100644 admin/public/votes.html create mode 100644 admin/public/votes.js (limited to 'admin') diff --git a/admin/public/index.html b/admin/public/index.html index a611291..815b6aa 100644 --- a/admin/public/index.html +++ b/admin/public/index.html @@ -11,22 +11,19 @@ /> - Home + Admin
    Home Logout
    - -
    - Welche/r Schüler/in... - - Welche/r Lehrer/in... - +

    Admin

    +
    - - diff --git a/admin/public/ranking.html b/admin/public/ranking.html new file mode 100644 index 0000000..328f09a --- /dev/null +++ b/admin/public/ranking.html @@ -0,0 +1,32 @@ + + + + + + + + + Home + + +
    + Home + Logout +
    + + +
    + Welche/r Schüler/in... + + Welche/r Lehrer/in... + +
    + + + + diff --git a/admin/public/ranking.js b/admin/public/ranking.js new file mode 100644 index 0000000..4281e23 --- /dev/null +++ b/admin/public/ranking.js @@ -0,0 +1,26 @@ +fetch("api/questions") + .then((questions) => questions.json()) + .then((questions) => { + fetch("api/answers") + .then((answers) => answers.json()) + .then((answers) => { + questions.forEach((question) => (question.answers = [])); + answers.forEach((answer) => questions[answer.question_id - 1].answers.push(answer)); + render(questions); + }); + }); + +function render(questions) { + const teacher = document.querySelector("ul#teacher"); + const pupil = document.querySelector("ul#pupil"); + questions.forEach((question) => { + const list = question.type === "teacher" ? teacher : pupil; + let answers = ""; + question.answers.forEach((answer) => { + answers += `
  • ${answer.name} ${answer.middlename ? answer.middlename + " " : ""}${answer.surname}: ${ + answer.count + }
  • `; + }); + list.insertAdjacentHTML("beforeend", `
  • ${question.question}
      ${answers}
  • `); + }); +} diff --git a/admin/public/script.js b/admin/public/script.js deleted file mode 100644 index d9fd30e..0000000 --- a/admin/public/script.js +++ /dev/null @@ -1,27 +0,0 @@ -fetch("api/questions") - .then((questions) => questions.json()) - .then((questions) => { - fetch("api/answers") - .then((answers) => answers.json()) - .then((answers) => { - questions.forEach((question) => (question.answers = [])); - answers.forEach((answer) => questions[answer.question_id - 1].answers.push(answer)); - render(questions); - }); - }); - -function render(questions) { - console.log(questions); - const teacher = document.querySelector("ul#teacher"); - const pupil = document.querySelector("ul#pupil"); - questions.forEach((question) => { - const list = question.type === "teacher" ? teacher : pupil; - let answers = ""; - question.answers.forEach((answer) => { - answers += `
  • ${answer.name} ${answer.middlename ? answer.middlename + " " : ""}${answer.surname}: ${ - answer.count - }
  • `; - }); - list.insertAdjacentHTML("beforeend", `
  • ${question.question}
      ${answers}
  • `); - }); -} diff --git a/admin/public/style.css b/admin/public/style.css index 77853bf..4e3cffc 100644 --- a/admin/public/style.css +++ b/admin/public/style.css @@ -12,7 +12,7 @@ body { position: absolute; max-height: 80%; overflow: auto; - width: 30%; + width: 40%; left: 50%; top: 50%; -webkit-transform: translate(-50%, -50%); @@ -26,7 +26,7 @@ div { background: white; } -@media only screen and (max-width: 600px) { +@media only screen and (max-width: 800px) { .card { width: calc(100% - 50px); } diff --git a/admin/public/votes.html b/admin/public/votes.html new file mode 100644 index 0000000..cd9563c --- /dev/null +++ b/admin/public/votes.html @@ -0,0 +1,28 @@ + + + + + + + + + Votes + + +
    + Home + Logout +
    +
    + +
    + + + + + diff --git a/admin/public/votes.js b/admin/public/votes.js new file mode 100644 index 0000000..3f1284c --- /dev/null +++ b/admin/public/votes.js @@ -0,0 +1,37 @@ +fetch("api/votes") + .then((response) => response.json()) + .then((response) => { + const ctx = document.getElementById("votes").getContext("2d"); + new Chart(ctx, { + type: "bar", + data: { + labels: response.map((v) => v.name), + datasets: [ + { + label: "# of Votes", + data: response.map((v) => v.votes || 0), + backgroundColor: () => "#" + (Math.random().toString(16) + "0000000").slice(2, 8), + borderWidth: 1, + }, + ], + }, + options: { + legend: { + display: false, + }, + tooltips: { + enabled: false, + }, + scales: { + yAxes: [ + { + ticks: { + beginAtZero: true, + precision: 0, + }, + }, + ], + }, + }, + }); + }); -- cgit v1.2.3 From 00409448b34265d976485095eadc579bde5cab57 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Sat, 10 Oct 2020 18:17:40 +0200 Subject: "Tob dich aus, Marvin!" --- admin/public/votes.html | 2 ++ admin/public/votes.js | 72 +++++++++++++++++++++++++++++-------------------- 2 files changed, 45 insertions(+), 29 deletions(-) (limited to 'admin') diff --git a/admin/public/votes.html b/admin/public/votes.html index cd9563c..b700ed9 100644 --- a/admin/public/votes.html +++ b/admin/public/votes.html @@ -19,6 +19,8 @@ Logout
    + +
    diff --git a/admin/public/votes.js b/admin/public/votes.js index 3f1284c..62ff374 100644 --- a/admin/public/votes.js +++ b/admin/public/votes.js @@ -1,37 +1,51 @@ +let date; +let chart; + fetch("api/votes") .then((response) => response.json()) .then((response) => { - const ctx = document.getElementById("votes").getContext("2d"); - new Chart(ctx, { - type: "bar", - data: { - labels: response.map((v) => v.name), - datasets: [ + data = response; + render("bar"); + }); + +function render(type) { + const ctx = document.getElementById("votes").getContext("2d"); + chart = new Chart(ctx, { + type, + data: { + labels: data.map((v) => v.name), + datasets: [ + { + label: "# of Votes", + data: data.map((v) => v.votes || 0), + backgroundColor: () => "#" + (Math.random().toString(16) + "0000000").slice(2, 8), + borderWidth: 1, + }, + ], + }, + options: { + legend: { + display: false, + }, + scales: { + yAxes: [ { - label: "# of Votes", - data: response.map((v) => v.votes || 0), - backgroundColor: () => "#" + (Math.random().toString(16) + "0000000").slice(2, 8), - borderWidth: 1, + ticks: { + beginAtZero: true, + precision: 0, + }, }, ], }, - options: { - legend: { - display: false, - }, - tooltips: { - enabled: false, - }, - scales: { - yAxes: [ - { - ticks: { - beginAtZero: true, - precision: 0, - }, - }, - ], - }, - }, - }); + }, }); +} + +let index = 0; +const types = ["pie", "doughnut", "polarArea", "radar", "line", "bar"]; +document.getElementById("switch").addEventListener("click", () => { + chart.destroy(); + render(types[index]); + if (index + 1 < types.length) index++; + else index = 0; +}); -- cgit v1.2.3