diff --git a/web/components/visualization/results-section.js b/web/components/visualization/results-section.js index 6a2cb5758a8c7592f65907e5b4ac371c0755e7e7..78f51e6bcfd05cc3bff86e4c0e79e0e4edf4f491 100644 --- a/web/components/visualization/results-section.js +++ b/web/components/visualization/results-section.js @@ -100,7 +100,14 @@ class ResultComponent { </header> <div class="card-content"> <div id="detailed-results" class="content"> - <h5 class="title is-5">Résultats détaillés du tour</h5> + <div class="control select"> + <select id="select-areas" class="input"></select> + </div> + <div class="control select"> + <select id="select-sections" class="input"></select> + </div> + <div id="zone-results"></div> + <div id="zone-detaileds-results" style="display:none"></div> </div> </div> </div> @@ -129,14 +136,27 @@ class ResultComponent { > ${candidateList.Name + " (" + - candidateList.VoiceNumber + - " voix soit : " + + (candidateList.VoiceNumber != null + ? candidateList.VoiceNumber + " votes soit : " + : "") + candidateList.Percentage + "%)"} </div> </div>`; } + showWarningResults(text) { + return /* HTML */ `<br /> + <article class="message is-warning"> + <div class="message-header"> + <p>Warning</p> + </div> + <div class="message-body"> + ${text} + </div> + </article>`; + } + handleDom() { let resultHandler = this; document.getElementById("areas").addEventListener("click", function () { @@ -175,6 +195,35 @@ class ResultComponent { this.displayResults(); }); } + + document.addEventListener( + "input", + function __listener(event) { + if (event.target.id == "select-areas") { + if (event.target.value != 0) { + let area = resultHandler.results.areasResults.find( + (area) => area.ID == event.target.value + ); + + if (resultHandler.zone === "areas") + resultHandler.displayZoneResults(area); + else if (resultHandler.zone === "sections") { + resultHandler.refreshSections(area); + resultHandler.displayZoneResults(area.Sections[0]); + } + } + } else if (event.target.id == "select-sections") { + let area = resultHandler.results.areasResults.find( + (area) => area.ID == document.getElementById("select-areas").value + ); + let section = area.Sections.find( + (section) => section.ID == event.target.value + ); + resultHandler.displayZoneResults(section); + } + }, + false + ); } zoomMap() { @@ -205,6 +254,7 @@ class ResultComponent { let resultHandler = this; document.getElementById("results-section").parentElement.className = "column is-full"; + document.getElementById("results-section").style.height = "70vh"; document.getElementById("map-section").parentElement.className = "column"; document.getElementById("map-section").parentElement.style.display = "none"; document.getElementById("news-flow-section").style.display = "none"; @@ -213,18 +263,21 @@ class ResultComponent { .addEventListener("click", function () { resultHandler.unZoom(); }); + document.getElementById("zone-detaileds-results").style.display = "block"; } unZoom() { document.getElementById("map-section").parentElement.className = "column is-half"; document.getElementById("news-flow-section").style.height = "45vh"; + document.getElementById("results-section").style.height = "25vh"; document.getElementById("news-flow-section").parentElement.className = "column is-half"; document.getElementById("map-section").parentElement.style.display = "block"; document.getElementById("results-section").style.display = "block"; document.getElementById("news-flow-section").style.display = "block"; + document.getElementById("zone-detaileds-results").style.display = "none"; this.handleDom(); } @@ -238,10 +291,11 @@ class ResultComponent { document.getElementById("news-flow").innerHTML = ""; if (this.zone === "areas") { this.displayFlowAreas(); + this.displayAreasResults(); } else if (this.zone === "sections") { this.displayFlowSections(); + this.displaySectionsResults(); } - this.displayRoundResults(); Scroller.scrollInit("news-flow"); } @@ -305,7 +359,9 @@ class ResultComponent { } displayRoundResults() { - document.getElementById("detailed-results").innerHTML = `<h5 class="title is-5">Résultats détaillés du tour</h5>`; + document.getElementById( + "detailed-results" + ).innerHTML = `<h5 class="title is-5">Résultats détaillés du tour</h5>`; for (let i in this.results.roundResults) { document.getElementById( "detailed-results" @@ -315,4 +371,146 @@ class ResultComponent { ); } } + + displayAreasResults() { + let selectAreas = document.getElementById("select-areas"); + document.getElementById("select-sections").parentNode.style.display = + "none"; + + for (let i = selectAreas.options.length - 1; i >= 0; i--) { + selectAreas.remove(i); + } + + this.results.areasResults.forEach((area) => { + let el = document.createElement("option"); + el.textContent = area.Name; + el.value = area.ID; + selectAreas.appendChild(el); + }); + + this.displayZoneResults(this.results.areasResults[0]); + } + + displaySectionsResults() { + let selectAreas = document.getElementById("select-areas"); + document.getElementById("select-sections").parentNode.style.display = + "block"; + + for (let i = selectAreas.options.length - 1; i >= 0; i--) { + selectAreas.remove(i); + } + + this.results.areasResults.forEach((area) => { + let el = document.createElement("option"); + el.textContent = area.Name; + el.value = area.ID; + selectAreas.appendChild(el); + }); + + this.refreshSections(this.results.areasResults[0]); + + this.displayZoneResults(this.results.areasResults[0].Sections[0]); + } + + async displayZoneResults(zone) { + if (zone.status !== this.filter) { + console.log(zone); + if (zone.status == "no_results") { + document.getElementById( + "zone-results" + ).innerHTML = this.showWarningResults( + "Aucun résultats n'ont étaient saisis sur cette zone" + ); + } else if (zone.status == "incompleted") { + document.getElementById( + "zone-results" + ).innerHTML = this.showWarningResults( + "Les résultats pour cette zone ne sont pas complets" + ); + } else if (zone.status == "not validated") { + document.getElementById( + "zone-results" + ).innerHTML = this.showWarningResults( + "Les résultats pour cette zone n'ont pas étaient validés" + ); + } + document.getElementById("zone-detaileds-results").innerHTML = ""; + } else { + document.getElementById("zone-results").innerHTML = + '<br/><h5 class="title is-5">Résultats</h5>'; + for (let i in zone.candidateLists) { + let party = await this.PartyModel.getParty( + zone.candidateLists[i].PartyID + ); + document.getElementById( + "zone-results" + ).innerHTML += this.progressBarTemplate( + zone.candidateLists[i], + party.Color + ); + } + this.displayZoneDetailedResults(zone); + } + } + + displayZoneDetailedResults(zone) { + document.getElementById("zone-detaileds-results").innerHTML = + '<br/><h5 class="title is-5">Statistiques</h5>'; + + if (this.filter === "partial") + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.progressBarTemplate( + { + Name: "Pourcentage de saisie", + Percentage: zone.stats.PercentageConsiderated, + VoiceNumber: null, + }, + "grey" + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.progressBarTemplate( + { + Name: "Abstention", + Percentage: zone.stats.Abstention, + VoiceNumber: null, + }, + "grey" + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.progressBarTemplate( + { + Name: "Votes blancs", + Percentage: zone.stats.BlankPercentage, + VoiceNumber: zone.stats.BlankVoiceNumber, + }, + "grey" + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.progressBarTemplate( + { + Name: "Votes nuls", + Percentage: zone.stats.NullVotePercentage, + VoiceNumber: zone.stats.NullVoteVoiceNumber, + }, + "grey" + ); + } + + refreshSections(area) { + let selectSections = document.getElementById("select-sections"); + selectSections.parentNode.style.display = "block"; + for (let i = selectSections.options.length - 1; i >= 0; i--) { + selectSections.remove(i); + } + for (let i in area.Sections) { + let el = document.createElement("option"); + el.textContent = area.Sections[i].Name; + el.value = area.Sections[i].ID; + selectSections.appendChild(el); + } + } } diff --git a/web/services/election/calculate-election-generic.js b/web/services/election/calculate-election-generic.js index a10fe1e68b89d5aab2ecf0a56182f4db3f58ffce..a6aa96c60bd9872c2de964983016d6c99ebcc327 100644 --- a/web/services/election/calculate-election-generic.js +++ b/web/services/election/calculate-election-generic.js @@ -330,10 +330,15 @@ class DirectMetropolitanCalculator { let nullVote = 0; let totalVotes = 0; let VotesExpressed = 0; + let completed = 0; for (let i in deskRounds) { let desk = await this.DeskModel.getDesk(deskRounds[i].DeskID); subscribed += desk.Subscribed; + if (deskRounds[i].Completed) { + completed += desk.Subscribed; + console.log("test") + } deskRounds[i].Votes.forEach((vote) => { totalVotes += vote.VoiceNumber; if (vote.Blank) blank += vote.VoiceNumber; @@ -345,9 +350,12 @@ class DirectMetropolitanCalculator { Abstention: Number( ((subscribed - totalVotes) / subscribed) * 100 ).toFixed(2), - Blank: Number((blank / totalVotes) * 100).toFixed(2), - NullVote: Number((nullVote / totalVotes) * 100).toFixed(2), + BlankPercentage: Number((blank / totalVotes) * 100).toFixed(2), + BlankVoiceNumber: blank, + NullVotePercentage: Number((nullVote / totalVotes) * 100).toFixed(2), + NullVoteVoiceNumber: nullVote, VotesExpressed: VotesExpressed, + PercentageConsiderated: Number((completed / subscribed) * 100).toFixed(2), }; } diff --git a/web/style.css b/web/style.css index d7a5bc075f1b0bf37e034678f5de9a470d8b35ed..eaece0420e7cb78b2920e31723668cc8e88014ec 100644 --- a/web/style.css +++ b/web/style.css @@ -121,18 +121,22 @@ select { #round-desks .column, #candidate-lists .column, -#news-flow { +#news-flow, +#detailed-results { overflow-y: auto; } #news-flow, -#news-flow .content { +#detailed-results, +#news-flow .content, +#detailed-results .content { flex: 1; } #round-desks .columns, #candidate-lists .columns, -#news-flow-section .card-content { +#news-flow-section .card-content, +#results-section .card-content { max-height: 90%; display: flex; } @@ -171,11 +175,18 @@ select { margin-bottom: 15px; } +#results-section { + height: 25vh; + padding-bottom: 20px; +} + .progressBar { width: 99%; margin: 3px; background-color: lightgray; border-radius: 5px; + text-overflow: clip; + overflow: hidden; } .progressBarValue { @@ -184,3 +195,7 @@ select { padding: 4px; border-radius: 5px; } + +.is-warning .message-body { + border: solid; +}