From dca2cc3eeb176a27bd03a2527ea2c573d28a6000 Mon Sep 17 00:00:00 2001 From: Wolvan Date: Thu, 6 Jan 2022 19:52:53 +0100 Subject: [PATCH] Create results page The page displays the amount of votes, percentage and bars for quick visual comparison, as well as a pie chart created with google charts. --- README.md | 3 +- frontend/.eslintrc.json | 6 ++- frontend/html/result.html | 38 ++++++++++++++++++- frontend/static/css/result.css | 69 ++++++++++++++++++++++++++++++++++ frontend/static/js/result.js | 49 ++++++++++++++++++++++++ src/frontend.ts | 18 ++++++++- 6 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 frontend/static/css/result.css create mode 100644 frontend/static/js/result.js diff --git a/README.md b/README.md index 3a404f4..9f81927 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,5 @@ Execute the scripts with `npm run + + + -

Welcome to poll.horse!

-

{{ POLL_TITLE }} - Result

+
+

Poll.horse

+

Make voting on things simpler

+
+
+
ID: {{ POLL_ID }}
+
+
+
+ {{ POLL_TITLE }} +
+
+ {{ POLL_OPTION_DIVS }} +
+ + +
+
+ \ No newline at end of file diff --git a/frontend/static/css/result.css b/frontend/static/css/result.css new file mode 100644 index 0000000..b17127e --- /dev/null +++ b/frontend/static/css/result.css @@ -0,0 +1,69 @@ +main .notepad .poll-option { + height: 80px; + width: 100%; + padding-left: 0; + padding-right: 0; +} +main .notepad .poll-option .poll-option-info, +main .notepad .poll-option .progress { + height: 40px; +} +main .notepad .poll-option .poll-option-info { + padding-left: 10%; + border-bottom: 1px solid #b1b874; +} +main .notepad .poll-option .poll-option-info .poll-option-text { + display: inline-block; + vertical-align: top; + height: 100%; + width: calc(100% - 105px); +} +main .notepad .poll-option .poll-option-info .poll-option-votes, +main .notepad .poll-option .progress .poll-bar-text { + display: inline-block; + vertical-align: top; + height: 100%; + width: 105px; + line-height: 40px; + font-size: 1.5em; + text-align: right; +} +main .notepad .poll-option .poll-option-info .poll-option-votes::after { + content: "Votes"; + margin-left: 5px; + padding-right: 10px; +} +main .notepad .poll-option .progress { + width: 90%; + padding-left: 10%; +} +main .notepad .poll-option .progress .poll-bar { + vertical-align: top; + display: inline-block; + height: 100%; + width: calc(100% - 60px); +} +main .notepad .poll-option .progress .poll-bar .poll-bar-fill { + height: calc(100% - 5px); + margin-top: 2px; + background-color: #cf2828; + transition: width .3s; +} +main .notepad .poll-option .progress .poll-bar-text { + width: 50px; + padding-right: 10px; +} + +aside#chart { + position: absolute; + top: 0px; + right: -200px; + width: 200px; + height: 200px; +} + +@media screen and (max-width: 750px) { + aside#chart { + visibility: hidden !important; + } +} diff --git a/frontend/static/js/result.js b/frontend/static/js/result.js new file mode 100644 index 0000000..7fae574 --- /dev/null +++ b/frontend/static/js/result.js @@ -0,0 +1,49 @@ +"use strict"; + +const textFitOptions = { + multiLine: true +}; + +let drawChart = () => { /* STUB */ }; + +function domLoaded() { + textFit(document.querySelector(".poll-title"), textFitOptions); + document.querySelectorAll(".poll-option .poll-option-text").forEach(element => textFit(element, textFitOptions)); + + google.charts.load('current', {'packages':['corechart']}); + google.charts.setOnLoadCallback(() => { + const chartOptions = { + backgroundColor: "transparent", + legend: "none", + chartArea: { + width: "90%", + height: "90%", + } + }; + const chartEl = document.getElementById('chart'); + const chart = new google.visualization.PieChart(chartEl); + + drawChart = data => { + chart.draw( + google.visualization.arrayToDataTable( + [["Options", "Votes"]].concat(data) + ), chartOptions + ); + chartEl.style.visibility = "visible"; + }; + + try { + drawChart(JSON.parse(POLL_VOTE_DATA_STRING)); + } catch (error) { + // eh + } + }); + window.addEventListener("resize", function() { + textFit(document.querySelector(".poll-title"), textFitOptions); + document.querySelectorAll(".poll-option .poll-option-text").forEach(element => textFit(element, textFitOptions)); + }); +} + +if (document.readyState === "complete" || document.readyState === "loaded") domLoaded(); +else document.addEventListener('DOMContentLoaded', domLoaded); + diff --git a/src/frontend.ts b/src/frontend.ts index 00e24b8..7f388e3 100644 --- a/src/frontend.ts +++ b/src/frontend.ts @@ -104,10 +104,26 @@ export default function init(router: Router): void { (program.opts().backendBaseUrl || "http://localhost:" + program.opts().port) + "/_backend/poll-result/" + id ).then(r => r.json()) as PollResult; if (!poll || poll.error) return res.redirect("/"); + const totalVotes = Object.values(poll.votes).reduce((acc, cur) => acc + cur, 0); + const pollOptionsDivs = Object.entries(poll.votes).map(([option, votes]) => ` +
+
+
${ option }
${ votes }
+
+
+
+
+
${ Math.round((votes / totalVotes) * 100) }%
+
+
+ `).join(""); + await displayPage(req, res, "result.html", { "POLL_ID": id, "POLL_TITLE": poll.title, - + "POLL_OPTION_DIVS": pollOptionsDivs, + "POLL_VOTES_TOTAL": totalVotes, + "POLL_OPTION_VOTES": JSON.stringify(Object.entries(poll.votes)) }); } catch (error) { console.log(error);