diff --git a/web/components/visualization/results-general.js b/web/components/visualization/results-general.js new file mode 100644 index 0000000000000000000000000000000000000000..60493593b479ce1a63fced5a7731468c597c5e76 --- /dev/null +++ b/web/components/visualization/results-general.js @@ -0,0 +1,153 @@ +// Imports +import * as Auth from "/services/auth/auth.js"; +import * as results from "/services/election/calculate-election-generic.js"; +import * as PartyModel from "/services/model/party-model.js"; +import * as CandidateListModel from "/services/model/candidateList-model.js"; +import * as AreaModel from "/services/model/area-model.js"; +import * as Scroller from "/services/common/scroller.js"; +import * as ResultsZone from "/components/visualization/results-zone.js"; + +export async function mount(where, parent) { + const resultGeneralComponent = new ResultGeneralComponent(parent); + await resultGeneralComponent.mount(where); + return resultGeneralComponent; +} + +class ResultGeneralComponent { + constructor(parent) { + this.parent = parent; + this.PartyModel = PartyModel.getPartyModel(); + this.CandidateListModel = CandidateListModel.getCandidateListModel(); + this.AreaModel = AreaModel.getAreaModel(); + } + + async mount(where) { + this.PartyModel.current_user = await Auth.GetUser(); + this.CandidateListModel.current_user = await Auth.GetUser(); + this.AreaModel.current_user = await Auth.GetUser(); + const mountpoint = where; + document.getElementById(mountpoint).innerHTML = /* HTML */ ` + <div class="column is-half"> + <div class="card-no-hover"> + <header class="card-header"> + <p class="card-header-title"> + Résultats du tour + </p> + </header> + <div id="round-results" class="content"></div> + <div id="round-detaileds-results" class="content"></div> + </div> + </div> + <div class="column is-half"> + <div class="card-no-hover"> + <header class="card-header"> + <p class="card-header-title"> + Statistiques + </p> + </header> + <div id="stats-results" class="content"></div> + </div> + </div> + `; + this.handleDom(); + this.displayRoundResults(); + } + + handleDom() {} + + displayRoundResults() { + document.getElementById("round-results").innerHTML = ""; + if (this.parent.results.status == this.parent.filter) { + for (let i in this.parent.results.roundResults) { + let electeds = this.parent.results.candidateLists + .filter((candidateList) => { + return ( + candidateList.PartyID == this.parent.results.roundResults[i].ID + ); + }) + .reduce((electeds, candidateList) => { + return electeds + candidateList.SeatsAttributed; + }, 0); + document.getElementById( + "round-results" + ).innerHTML += this.parent.progressBarTemplate( + this.parent.results.roundResults[i], + this.parent.results.roundResults[i].Color, + electeds + ); + } + } else { + if (this.parent.results.status == "no_results") { + document.getElementById( + "round-results" + ).innerHTML = this.parent.showWarningResults( + "Aucun résultats n'ont étaient saisis sur cette zone" + ); + } else if (this.parent.results.status == "incompleted") { + document.getElementById( + "round-results" + ).innerHTML = this.parent.showWarningResults( + "Les résultats pour cette zone ne sont pas complets" + ); + } else if (this.parent.results.status == "not validated") { + document.getElementById( + "round-results" + ).innerHTML = this.parent.showWarningResults( + "Les résultats pour cette zone n'ont pas étaient validés" + ); + } + } + this.displayRoundDetailedResults(); + } + + async displayRoundDetailedResults() { + document.getElementById("round-detaileds-results").innerHTML = + '<br/><h5 class="title is-5">Statistiques</h5>'; + + if (this.parent.filter === "partial") + document.getElementById( + "round-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Pourcentage de saisie", + Percentage: this.parent.results.stats.PercentageConsiderated, + VoiceNumber: null, + }, + "grey", + null + ); + document.getElementById( + "round-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Abstention", + Percentage: this.parent.results.stats.Abstention, + VoiceNumber: null, + }, + "grey", + null + ); + document.getElementById( + "round-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Votes blancs", + Percentage: this.parent.results.stats.BlankPercentage, + VoiceNumber: this.parent.results.stats.BlankVoiceNumber, + }, + "grey", + null + ); + document.getElementById( + "round-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Votes nuls", + Percentage: this.parent.results.stats.NullVotePercentage, + VoiceNumber: this.parent.results.stats.NullVoteVoiceNumber, + }, + "grey", + null + ); + } +} diff --git a/web/components/visualization/results-section.js b/web/components/visualization/results-section.js index d11babdc97edbcc72c6f04c9bac0e588390c085f..45a5c4a036fda33956410ba567b04968d3454923 100644 --- a/web/components/visualization/results-section.js +++ b/web/components/visualization/results-section.js @@ -4,7 +4,8 @@ import * as results from "/services/election/calculate-election-generic.js"; import * as PartyModel from "/services/model/party-model.js"; import * as CandidateListModel from "/services/model/candidateList-model.js"; import * as AreaModel from "/services/model/area-model.js"; -import * as Scroller from "/services/common/scroller.js"; +import * as ResultsZone from "/components/visualization/results-zone.js"; +import * as ResultsGeneral from "/components/visualization/results-general.js"; export async function mount(where, round) { const resultComponent = new ResultComponent(round); @@ -27,7 +28,12 @@ class ResultComponent { document.getElementById(mountpoint).innerHTML = /* HTML */ ` <div class="tabs is-boxed is-toggle is-fullwidth"> <ul> - <li id="areas" class="is-active"> + <li id="general" class="is-active"> + <a> + <span>Général</span> + </a> + </li> + <li id="areas"> <a> <span>Circonscriptions</span> </a> @@ -53,97 +59,14 @@ class ResultComponent { Validé </label> </div> - <div class="columns"> - <div class="column is-half"> - <div id="map-section" class="card-no-hover"> - <header class="card-header"> - <p class="card-header-title"> - Carte - </p> - <button id="zoom-map" class="button is-success"> - <span class="icon is-small"> - <i class="fas fa-search"></i> - </span> - </button> - </header> - <div class="card-content"> - <div id="round-list" class="content"> - La carte du tour - </div> - </div> - </div> - </div> - <div class="column"> - <div id="news-flow-section" class="card-no-hover"> - <header class="card-header"> - <p class="card-header-title"> - Actualités - </p> - <button id="zoom-news-flow" class="button is-success"> - <span class="icon is-small"> - <i class="fas fa-search"></i> - </span> - </button> - </header> - <div class="card-content"> - <div id="news-flow" class="content"> - Flux d'actualité - </div> - </div> - </div> - <div id="results-section" class="card-no-hover" "> - <header class="card-header"> - <p class="card-header-title"> - Résultats - </p> - <button id="zoom-results" class="button is-success"> - <span class="icon is-small"> - <i class="fas fa-search"></i> - </span> - </button> - </header> - <div class="card-content"> - <div id="detailed-results" class="content"> - <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> - </div> + <div id="results-general" class="columns"></div> + <div id="results-zone" class="columns" style="display: none;"></div> `; this.calculator = await results.mountCalculator(this.round); + await this.calculateResults(); + this.resultsZone = await ResultsZone.mount("results-zone", this); + this.resultsGeneral = await ResultsGeneral.mount("results-general", this); this.handleDom(); - document.getElementById("areas").click(); - } - - resultFlowTemplate(zone) { - let html = document.createElement("div"); - html.classList = "card-list card-no-hover"; - html.innerHTML = /* HTML */ ` - <div class="card-content"> - <div id="flow-content-${zone.ID}" class="content"> - <h5 class="title is-5">${zone.Name}</h5> - </div> - </div> - `; - html.addEventListener("click", async () => { - if (this.zone == "sections") { - let area = await this.AreaModel.getArea(zone.AreaID); - this.refreshSections(area); - document.getElementById("select-sections").value = zone.ID; - document.getElementById("select-areas").value = zone.AreaID; - } else { - document.getElementById("select-areas").value = zone.ID; - } - this.displayZoneResults(zone); - }); - return html; } progressBarTemplate(candidateList, color, electedsNumber) { @@ -178,44 +101,47 @@ class ResultComponent { handleDom() { let resultHandler = this; + document + .getElementById("general") + .addEventListener("click", async function () { + document.getElementById("results-zone").style.display = "none"; + document.getElementById("results-general").style.display = "flex"; + await resultHandler.calculateResults(); + document.getElementById("sections").setAttribute("class", ""); + document.getElementById("areas").setAttribute("class", ""); + document.getElementById("general").setAttribute("class", "is-active"); + }); document .getElementById("areas") .addEventListener("click", async function () { + document.getElementById("results-zone").style.display = "flex"; + document.getElementById("results-general").style.display = "none"; resultHandler.zone = "areas"; await resultHandler.calculateResults(); - resultHandler.displayResults(); + resultHandler.resultsZone.displayResults(); document.getElementById("sections").setAttribute("class", ""); + document.getElementById("general").setAttribute("class", ""); document.getElementById("areas").setAttribute("class", "is-active"); }); document .getElementById("sections") .addEventListener("click", async function () { + document.getElementById("results-zone").style.display = "flex"; + document.getElementById("results-general").style.display = "none"; resultHandler.zone = "sections"; await resultHandler.calculateResults(); - resultHandler.displayResults(); + resultHandler.resultsZone.displayResults(); document.getElementById("areas").setAttribute("class", ""); + document.getElementById("general").setAttribute("class", ""); document.getElementById("sections").setAttribute("class", "is-active"); }); - document.getElementById("zoom-map").addEventListener("click", function () { - resultHandler.zoomMap(); - }); - document - .getElementById("zoom-news-flow") - .addEventListener("click", function () { - resultHandler.zoomNewsFlow(); - }); - document - .getElementById("zoom-results") - .addEventListener("click", function () { - resultHandler.zoomResults(); - }); - let radioButtons = document.getElementsByName("filter"); for (var i = 0; i < radioButtons.length; i++) { radioButtons[i].addEventListener("click", async (e) => { await this.calculateResults(); - this.displayResults(); + this.resultsZone.displayResults(); + this.resultsGeneral.displayRoundResults(); }); } @@ -229,10 +155,10 @@ class ResultComponent { ); if (resultHandler.zone === "areas") - resultHandler.displayZoneResults(area); + resultHandler.resultsZone.displayZoneResults(area); else if (resultHandler.zone === "sections") { resultHandler.refreshSections(area); - resultHandler.displayZoneResults(area.Sections[0]); + resultHandler.resultsZone.displayZoneResults(area.Sections[0]); } } } else if (event.target.id == "select-sections") { @@ -242,145 +168,18 @@ class ResultComponent { let section = area.Sections.find( (section) => section.ID == event.target.value ); - resultHandler.displayZoneResults(section); + resultHandler.resultsZone.displayZoneResults(section); } }, false ); } - zoomMap() { - let resultHandler = this; - document.getElementById("map-section").parentElement.className = - "column is-full"; - document.getElementById("zoom-map").addEventListener("click", function () { - resultHandler.unZoom(); - }); - } - - zoomNewsFlow() { - let resultHandler = this; - document.getElementById("news-flow-section").parentElement.className = - "column is-full"; - document.getElementById("news-flow-section").style.height = "70vh"; - document.getElementById("map-section").parentElement.className = "column"; - document.getElementById("map-section").parentElement.style.display = "none"; - document.getElementById("results-section").style.display = "none"; - document - .getElementById("zoom-news-flow") - .addEventListener("click", function () { - resultHandler.unZoom(); - }); - } - - zoomResults() { - 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"; - document - .getElementById("zoom-results") - .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(); - } - async calculateResults() { this.filter = document.querySelector('input[name="filter"]:checked').value; this.results = await this.calculator.calculateResults(this.filter); } - displayResults() { - document.getElementById("news-flow").innerHTML = ""; - if (this.zone === "areas") { - this.displayFlowAreas(); - this.displayAreasResults(); - } else if (this.zone === "sections") { - this.displayFlowSections(); - this.displaySectionsResults(); - } - Scroller.scrollInit("news-flow"); - } - - async displayFlowAreas() { - this.results.areasResults.sort(function (a, b) { - return b.DateCompletion - a.DateCompletion; - }); - for (let j in this.results.areasResults) { - let area = this.results.areasResults[j]; - if (area.status === this.filter) { - document - .getElementById("news-flow") - .appendChild(this.resultFlowTemplate(area)); - - for (let i in area.candidateLists) { - let party = await this.PartyModel.getParty( - area.candidateLists[i].PartyID - ); - - document.getElementById( - "flow-content-" + area.ID - ).innerHTML += await this.progressBarTemplate( - area.candidateLists[i], - party.Color - ); - } - } - } - } - - async displayFlowSections() { - let sections = []; - this.results.areasResults.forEach((area) => { - sections = sections.concat(area.Sections); - }); - sections.sort(function (a, b) { - return b.DateCompletion - a.DateCompletion; - }); - - for (let j in sections) { - let section = sections[j]; - if (section.status === this.filter) { - document - .getElementById("news-flow") - .appendChild(this.resultFlowTemplate(section)); - - for (let i in section.candidateLists) { - let party = await this.PartyModel.getParty( - section.candidateLists[i].PartyID - ); - - document.getElementById( - "flow-content-" + section.ID - ).innerHTML += await this.progressBarTemplate( - section.candidateLists[i], - party.Color - ); - } - } - } - } - displayRoundResults() { document.getElementById( "detailed-results" @@ -395,157 +194,6 @@ 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) { - 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 electedsNumber = null; - if ( - this.zone === "areas" && - zone.candidateLists[i].SeatsAttributed > 0 && - zone.stats.PercentageConsiderated == 100 - ) { - electedsNumber = zone.candidateLists[i].SeatsAttributed; - } - let party = await this.PartyModel.getParty( - zone.candidateLists[i].PartyID - ); - document.getElementById( - "zone-results" - ).innerHTML += this.progressBarTemplate( - zone.candidateLists[i], - party.Color, - electedsNumber - ); - } - this.displayZoneDetailedResults(zone); - } - } - - async 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", - null - ); - document.getElementById( - "zone-detaileds-results" - ).innerHTML += this.progressBarTemplate( - { - Name: "Abstention", - Percentage: zone.stats.Abstention, - VoiceNumber: null, - }, - "grey", - null - ); - document.getElementById( - "zone-detaileds-results" - ).innerHTML += this.progressBarTemplate( - { - Name: "Votes blancs", - Percentage: zone.stats.BlankPercentage, - VoiceNumber: zone.stats.BlankVoiceNumber, - }, - "grey", - null - ); - document.getElementById( - "zone-detaileds-results" - ).innerHTML += this.progressBarTemplate( - { - Name: "Votes nuls", - Percentage: zone.stats.NullVotePercentage, - VoiceNumber: zone.stats.NullVoteVoiceNumber, - }, - "grey", - null - ); - if (this.zone === "areas" && zone.stats.PercentageConsiderated == 100) { - document.getElementById("zone-detaileds-results").innerHTML += - '<br/><h5 class="title is-5">Élus</h5>'; - for (let i in zone.Electeds) { - let candidateList = await this.CandidateListModel.getCandidateList( - zone.Electeds[i].CandidateListID - ); - document.getElementById("zone-detaileds-results").innerHTML += - zone.Electeds[i].FullName + " (" + candidateList.Name + ")<br/>"; - } - } - } - refreshSections(area) { let selectSections = document.getElementById("select-sections"); selectSections.parentNode.style.display = "block"; diff --git a/web/components/visualization/results-zone.js b/web/components/visualization/results-zone.js new file mode 100644 index 0000000000000000000000000000000000000000..6092a166e7edafe2d353fdb27deff63f562c35c8 --- /dev/null +++ b/web/components/visualization/results-zone.js @@ -0,0 +1,462 @@ +// Imports +import * as Auth from "/services/auth/auth.js"; +import * as results from "/services/election/calculate-election-generic.js"; +import * as PartyModel from "/services/model/party-model.js"; +import * as CandidateListModel from "/services/model/candidateList-model.js"; +import * as AreaModel from "/services/model/area-model.js"; +import * as Scroller from "/services/common/scroller.js"; + +export async function mount(where, parent) { + const resultZoneComponent = new ResultZoneComponent(parent); + await resultZoneComponent.mount(where); + return resultZoneComponent; +} + +class ResultZoneComponent { + constructor(parent) { + this.parent = parent; + this.PartyModel = PartyModel.getPartyModel(); + this.CandidateListModel = CandidateListModel.getCandidateListModel(); + this.AreaModel = AreaModel.getAreaModel(); + } + + async mount(where) { + this.PartyModel.current_user = await Auth.GetUser(); + this.CandidateListModel.current_user = await Auth.GetUser(); + this.AreaModel.current_user = await Auth.GetUser(); + const mountpoint = where; + document.getElementById(mountpoint).innerHTML = /* HTML */ ` + <div class="column is-half"> + <div id="map-section" class="card-no-hover"> + <header class="card-header"> + <p class="card-header-title"> + Carte + </p> + <button id="zoom-map" class="button is-success"> + <span class="icon is-small"> + <i class="fas fa-search"></i> + </span> + </button> + </header> + <div class="card-content"> + <div id="round-list" class="content"> + La carte du tour + </div> + </div> + </div> + </div> + <div class="column"> + <div id="news-flow-section" class="card-no-hover"> + <header class="card-header"> + <p class="card-header-title"> + Actualités + </p> + <label class="checkbox"> + <input id="auto-scroll" type="checkbox" checked /> + Défilement automatique   + </label> + <button id="zoom-news-flow" class="button is-success"> + <span class="icon is-small"> + <i class="fas fa-search"></i> + </span> + </button> + </header> + <div class="card-content"> + <div id="news-flow" class="content"> + Flux d'actualité + </div> + </div> + </div> + <div id="results-section" class="card-no-hover" "> + <header class="card-header"> + <p class="card-header-title"> + Résultats + </p> + <button id="zoom-results" class="button is-success"> + <span class="icon is-small"> + <i class="fas fa-search"></i> + </span> + </button> + </header> + <div class="card-content"> + <div id="detailed-results" class="content"> + <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> + `; + this.handleDom(); + } + + resultFlowTemplate(zone) { + let html = document.createElement("div"); + html.classList = "card-list card-no-hover"; + html.innerHTML = /* HTML */ ` + <div class="card-content"> + <div id="flow-content-${zone.ID}" class="content"> + <h5 class="title is-5">${zone.Name}</h5> + </div> + </div> + `; + html.addEventListener("click", async () => { + if (this.parent.zone == "sections") { + let area = await this.AreaModel.getArea(zone.AreaID); + this.parent.refreshSections(area); + document.getElementById("select-sections").value = zone.ID; + document.getElementById("select-areas").value = zone.AreaID; + } else { + document.getElementById("select-areas").value = zone.ID; + } + this.displayZoneResults(zone); + }); + return html; + } + + handleDom() { + let resultHandler = this; + document.getElementById("zoom-map").addEventListener("click", function () { + resultHandler.zoomMap(); + }); + document + .getElementById("zoom-news-flow") + .addEventListener("click", function () { + resultHandler.zoomNewsFlow(); + }); + document + .getElementById("zoom-results") + .addEventListener("click", function () { + resultHandler.zoomResults(); + }); + + let radioButtons = document.getElementsByName("filter"); + for (var i = 0; i < radioButtons.length; i++) { + radioButtons[i].addEventListener("click", async (e) => { + await this.parent.calculateResults(); + 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() { + let resultHandler = this; + document.getElementById("map-section").parentElement.className = + "column is-full"; + document.getElementById("zoom-map").addEventListener("click", function () { + resultHandler.unZoom(); + }); + } + + zoomNewsFlow() { + let resultHandler = this; + document.getElementById("news-flow-section").parentElement.className = + "column is-full"; + document.getElementById("news-flow-section").style.height = "70vh"; + document.getElementById("map-section").parentElement.className = "column"; + document.getElementById("map-section").parentElement.style.display = "none"; + document.getElementById("results-section").style.display = "none"; + document + .getElementById("zoom-news-flow") + .addEventListener("click", function () { + resultHandler.unZoom(); + }); + } + + zoomResults() { + 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"; + document + .getElementById("zoom-results") + .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(); + } + + displayResults() { + document.getElementById("news-flow").innerHTML = ""; + if (this.parent.zone === "areas") { + this.displayFlowAreas(); + this.displayAreasResults(); + } else if (this.parent.zone === "sections") { + this.displayFlowSections(); + this.displaySectionsResults(); + } + let scroller = Scroller.scrollInit("news-flow"); + document.getElementById("auto-scroll").addEventListener("change", () => { + scroller.switch(); + }); + } + + async displayFlowAreas() { + this.parent.results.areasResults.sort(function (a, b) { + return b.DateCompletion - a.DateCompletion; + }); + for (let j in this.parent.results.areasResults) { + let area = this.parent.results.areasResults[j]; + if (area.status === this.parent.filter) { + document + .getElementById("news-flow") + .appendChild(this.resultFlowTemplate(area)); + + for (let i in area.candidateLists) { + let party = await this.PartyModel.getParty( + area.candidateLists[i].PartyID + ); + + document.getElementById( + "flow-content-" + area.ID + ).innerHTML += await this.parent.progressBarTemplate( + area.candidateLists[i], + party.Color + ); + } + } + } + } + + async displayFlowSections() { + let sections = []; + this.parent.results.areasResults.forEach((area) => { + sections = sections.concat(area.Sections); + }); + sections.sort(function (a, b) { + return b.DateCompletion - a.DateCompletion; + }); + + for (let j in sections) { + let section = sections[j]; + if (section.status === this.parent.filter) { + document + .getElementById("news-flow") + .appendChild(this.resultFlowTemplate(section)); + + for (let i in section.candidateLists) { + let party = await this.PartyModel.getParty( + section.candidateLists[i].PartyID + ); + + document.getElementById( + "flow-content-" + section.ID + ).innerHTML += await this.parent.progressBarTemplate( + section.candidateLists[i], + party.Color + ); + } + } + } + } + + 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.parent.results.areasResults.forEach((area) => { + let el = document.createElement("option"); + el.textContent = area.Name; + el.value = area.ID; + selectAreas.appendChild(el); + }); + + this.displayZoneResults(this.parent.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.parent.results.areasResults.forEach((area) => { + let el = document.createElement("option"); + el.textContent = area.Name; + el.value = area.ID; + selectAreas.appendChild(el); + }); + + this.parent.refreshSections(this.parent.results.areasResults[0]); + + this.displayZoneResults(this.parent.results.areasResults[0].Sections[0]); + } + + async displayZoneResults(zone) { + if (zone.status !== this.parent.filter) { + if (zone.status == "no_results") { + document.getElementById( + "zone-results" + ).innerHTML = this.parent.showWarningResults( + "Aucun résultats n'ont étaient saisis sur cette zone" + ); + } else if (zone.status == "incompleted") { + document.getElementById( + "zone-results" + ).innerHTML = this.parent.showWarningResults( + "Les résultats pour cette zone ne sont pas complets" + ); + } else if (zone.status == "not validated") { + document.getElementById( + "zone-results" + ).innerHTML = this.parent.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 electedsNumber = null; + if ( + this.parent.zone === "areas" && + zone.candidateLists[i].SeatsAttributed > 0 && + zone.stats.PercentageConsiderated == 100 + ) { + electedsNumber = zone.candidateLists[i].SeatsAttributed; + } + let party = await this.PartyModel.getParty( + zone.candidateLists[i].PartyID + ); + document.getElementById( + "zone-results" + ).innerHTML += this.parent.progressBarTemplate( + zone.candidateLists[i], + party.Color, + electedsNumber + ); + } + this.displayZoneDetailedResults(zone); + } + document.getElementById( + "detailed-results" + ).scrollTop = document.getElementById("zone-results").offsetTop; + } + + async displayZoneDetailedResults(zone) { + document.getElementById("zone-detaileds-results").innerHTML = + '<br/><h5 class="title is-5">Statistiques</h5>'; + + if (this.parent.filter === "partial") + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Pourcentage de saisie", + Percentage: zone.stats.PercentageConsiderated, + VoiceNumber: null, + }, + "grey", + null + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Abstention", + Percentage: zone.stats.Abstention, + VoiceNumber: null, + }, + "grey", + null + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Votes blancs", + Percentage: zone.stats.BlankPercentage, + VoiceNumber: zone.stats.BlankVoiceNumber, + }, + "grey", + null + ); + document.getElementById( + "zone-detaileds-results" + ).innerHTML += this.parent.progressBarTemplate( + { + Name: "Votes nuls", + Percentage: zone.stats.NullVotePercentage, + VoiceNumber: zone.stats.NullVoteVoiceNumber, + }, + "grey", + null + ); + if ( + this.parent.zone === "areas" && + zone.stats.PercentageConsiderated == 100 + ) { + document.getElementById("zone-detaileds-results").innerHTML += + '<br/><h5 class="title is-5">Élus</h5>'; + for (let i in zone.Electeds) { + let candidateList = await this.CandidateListModel.getCandidateList( + zone.Electeds[i].CandidateListID + ); + document.getElementById("zone-detaileds-results").innerHTML += + zone.Electeds[i].FullName + " (" + candidateList.Name + ")<br/>"; + } + } + } +} diff --git a/web/services/common/scroller.js b/web/services/common/scroller.js index 1db60448ef17116231fccdd282d8942f5c5f3800..7274c566fcf406bead57ea778ed397d5130088e6 100644 --- a/web/services/common/scroller.js +++ b/web/services/common/scroller.js @@ -1,5 +1,6 @@ export function scrollInit(divName) { let scroller = new Scroller(divName); + return scroller; } class Scroller { @@ -12,11 +13,12 @@ class Scroller { this.scrollDiv(); this.elmnt.addEventListener("mouseover", () => { - this.pauseScroll(); + if (this.autoScroll) this.pauseScroll(); }); this.elmnt.addEventListener("mouseout", () => { - this.resumeScroll(); + if (this.autoScroll) this.resumeScroll(); }); + this.autoScroll = true; } scrollDiv() { @@ -45,4 +47,14 @@ class Scroller { resumeScroll() { this.scrollDiv(); } + + switch() { + if (this.autoScroll) { + this.pauseScroll(); + this.autoScroll = false; + } else { + this.resumeScroll(); + this.autoScroll = true; + } + } } diff --git a/web/services/election/calculate-election-generic.js b/web/services/election/calculate-election-generic.js index eed1675625e11f967b3d2e4d12defc0d8f5c9357..02bfe178b721c9f1a09a8b0f06ed5a7b097144c5 100644 --- a/web/services/election/calculate-election-generic.js +++ b/web/services/election/calculate-election-generic.js @@ -362,7 +362,6 @@ class DirectMetropolitanCalculator { // order candidates by rank and remove refused or removed candidates area.candidateLists.forEach((candidateList) => { - candidateList.seatNumber = 0; candidateList.Candidates.sort(function (a, b) { return a.Rank - b.Rank; }); diff --git a/web/style.css b/web/style.css index eaece0420e7cb78b2920e31723668cc8e88014ec..15fbb41128a3addee01bbbc0b65c15ef897121c1 100644 --- a/web/style.css +++ b/web/style.css @@ -199,3 +199,8 @@ select { .is-warning .message-body { border: solid; } + +#round-results, +#round-detaileds-results { + padding: 20px; +}