From 1a53b291b7ef46b1979489f032133890d3d72514 Mon Sep 17 00:00:00 2001 From: Alexis POYEN <apoyen@grandlyon.com> Date: Thu, 11 Jun 2020 13:32:36 +0200 Subject: [PATCH] Resolve "Handle CandidateList" --- web/components/management/candidate-lists.js | 282 +++++++++++++++++-- web/components/management/round.js | 1 + web/components/management/rounds-card.js | 15 +- web/services/model/area-model.js | 2 +- web/services/model/candidateList-model.js | 100 +++++++ web/services/model/party-model.js | 2 +- 6 files changed, 376 insertions(+), 26 deletions(-) create mode 100644 web/services/model/candidateList-model.js diff --git a/web/components/management/candidate-lists.js b/web/components/management/candidate-lists.js index 5c9bab1..c58460c 100644 --- a/web/components/management/candidate-lists.js +++ b/web/components/management/candidate-lists.js @@ -1,6 +1,11 @@ // Imports import * as Auth from "/services/auth/auth.js"; +import * as Common from "/services/common/common.js"; +import * as Messages from "/services/messages/messages.js"; import * as AreaModel from "/services/model/area-model.js"; +import * as PartyModel from "/services/model/party-model.js"; +import * as CandidateListModel from "/services/model/candidateList-model.js"; +import { Delete } from "/services/common/delete.js"; // DOM elements @@ -18,10 +23,14 @@ class CandidateList { this.method = null; this.parent = parent; this.AreaModel = AreaModel.getAreaModel(); + this.PartyModel = PartyModel.getPartyModel(); + this.CandidateListModel = CandidateListModel.getCandidateListModel(); } async mount(where) { this.AreaModel.current_user = await Auth.GetUser(); + this.PartyModel.current_user = await Auth.GetUser(); + this.CandidateListModel.current_user = await Auth.GetUser(); const mountpoint = where; document.getElementById(mountpoint).innerHTML = /* HTML */ ` <header class="card-header"> @@ -34,22 +43,24 @@ class CandidateList { </span> </button> </header> - <div class="columns card-content"> - <div class="column"> - <div id="areas-round"></div> - </div> - <div class="column"> - <div id="desk-round-details"></div> - </div> - <div class="column"> - <div id="active-capturer" class="card"></div> - </div> - <div class="column"> - <div id="available-capturer" class="card"></div> - </div> + <div class="columns card-content"> + <div class="column"> + <div id="areas-round"></div> </div> + <div class="column"> + <div id="candidate-lists-list"></div> + </div> + <div class="column"> + <div id="active-capturer" class="card"></div> + </div> + <div class="column"> + <div id="available-capturer" class="card"></div> + </div> + </div> `; current_user = await Auth.GetUser(); + this.mountModal("candidateList-modal"); + this.handleDom(); } areaTemplate(area) { @@ -66,6 +77,127 @@ class CandidateList { </div>`; } + candidateListTemplate(candidateList) { + return /* HTML */ `<div class="card card-list"> + <div + id="candidateLists-candidateList-${candidateList.ID}" + class="card-content" + > + <div class="content"> + <nav class="level"> + <div + id="candidateLists-candidateList-desc-${candidateList.ID}" + class="level-left" + > + ${candidateList.Name} + </div> + <div class="level-right"> + <a + id="candidateLists-candidateList-edit-${candidateList.ID}" + class="button is-link is-small" + title="Modifier" + > + <span class="icon is-small"> + <i class="fas fa-pen"></i> + </span> + </a> + <a + id="candidateLists-candidateList-delete-${candidateList.ID}" + class="button is-danger is-small" + title="Supprimer" + > + <span class="icon is-small"> + <i class="fas fa-times"></i> + </span> + </a> + </div> + </nav> + </div> + </div> + </div>`; + } + + mountModal(where) { + const mountpoint = where; + document.getElementById(mountpoint).innerHTML = /* HTML */ ` + <div class="modal-background"></div> + <div class="modal-card" id="candidateList-modal-card"> + <header class="modal-card-head"> + <p class="modal-card-title">Ajout/modification d'un tour</p> + <button + class="delete" + aria-label="close" + id="candidateList-modal-close" + ></button> + </header> + <section class="modal-card-body"> + <div class="field"> + <label>Id</label> + <div class="control"> + <input + class="input" + type="number" + id="candidateList-modal-id" + disabled + /> + </div> + </div> + <div class="field"> + <label>Name</label> + <div class="control"> + <input class="input" type="text" id="candidateList-modal-name" /> + </div> + </div> + <div class="field"> + <label>Parti</label><br /> + <div class="control select"> + <select name="party" id="candidateList-modal-party"></select> + </div> + </div> + </section> + <footer class="modal-card-foot"> + <button id="candidateList-modal-save" class="button is-success"> + Sauvegarder + </button> + <button id="candidateList-modal-cancel" class="button"> + Annuler + </button> + </footer> + </div> + `; + } + + handleDom() { + let candidateListHandler = this; + document + .getElementById(`candidate-list-new`) + .addEventListener("click", function () { + candidateListHandler.newCandidateList(); + }); + + document + .getElementById(`candidateList-modal-close`) + .addEventListener("click", function () { + Common.toggleModal("candidateList-modal", "candidateList-modal-card"); + }); + document + .getElementById(`candidateList-modal-cancel`) + .addEventListener("click", function () { + Common.toggleModal("candidateList-modal", "candidateList-modal-card"); + }); + document + .getElementById(`candidateList-modal-save`) + .addEventListener("click", async function () { + if ( + document.getElementById("candidateList-modal-party").value === "0" + ) { + Messages.Show("is-danger", "Veuillez sélectionner un parti."); + return; + } + await candidateListHandler.saveCandidateList(); + }); + } + async displayAreas() { let candidateListHandler = this; let areas = await this.updateAreas(); @@ -77,7 +209,6 @@ class CandidateList { .getElementById(`areas-area-${area.ID}`) .addEventListener("click", async function () { await candidateListHandler.activateArea(area); - candidateListHandler.area = area; await candidateListHandler.displayCandidateLists(); }); }); @@ -91,11 +222,130 @@ class CandidateList { }); } - async activateArea(){ + async activateArea(areaToActivate) { + this.area = areaToActivate; + let areas = await this.updateAreas(); + areas.forEach((area) => { + document + .getElementById(`areas-area-${area.ID}`) + .classList.remove("active-card"); + }); + document + .getElementById(`areas-area-${areaToActivate.ID}`) + .classList.add("active-card"); + document.getElementById("candidate-list-new").removeAttribute("disabled"); + } + + async activateCandidateList() {} + + async displayCandidateLists() { + let candidateListHandler = this; + let candidateLists = await this.updateCandidateLists(); + const markup = candidateLists + .map((candidateList) => this.candidateListTemplate(candidateList)) + .join(""); + document.getElementById("candidate-lists-list").innerHTML = markup; + candidateLists.map(async (candidateList) => { + let party = await this.PartyModel.getParty(candidateList.PartyID); + document.getElementById( + `candidateLists-candidateList-desc-${candidateList.ID}` + ).innerHTML += "(" + party.Name + ")"; + document + .getElementById(`candidateLists-candidateList-edit-${candidateList.ID}`) + .addEventListener("click", function () { + candidateListHandler.editCandidateList(candidateList); + }); + document + .getElementById( + `candidateLists-candidateList-delete-${candidateList.ID}` + ) + .addEventListener("click", function () { + new Delete(() => { + candidateListHandler.deleteCandidateList(candidateList); + }); + }); + // document + // .getElementById(`areas-area-${candidateList.ID}`) + // .addEventListener("click", async function () { + // await candidateListHandler.activateArea(candidateList); + // candidateListHandler.area = candidateList; + // await candidateListHandler.displayCandidateLists(); + // }); + }); + } + + async updateCandidateLists() { + let candidateListHandler = this; + let candidateLists = await this.CandidateListModel.getCandidateLists(); + return candidateLists.filter(function (candidateList) { + return candidateList.AreaID == candidateListHandler.area.ID; + }); + } + + async newCandidateList() { + this.method = "POST"; + await this.refreshParties(); + document.getElementById("candidateList-modal-id").value = null; + document.getElementById("candidateList-modal-party").value = null; + document.getElementById("candidateList-modal-name").value = null; + Common.toggleModal("candidateList-modal", "candidateList-modal-card"); } - async displayCandidateLists(){ + async editCandidateList(candidateList) { + this.method = "PUT"; + await this.refreshParties(); + document.getElementById("candidateList-modal-id").value = candidateList.ID; + document.getElementById("candidateList-modal-party").value = + candidateList.PartyID; + document.getElementById("candidateList-modal-name").value = + candidateList.Name; + Common.toggleModal("candidateList-modal", "candidateList-modal-card"); + } + + async refreshParties() { + let selectParties = document.getElementById("candidateList-modal-party"); + let parties = await this.PartyModel.getParties(); + for (let i = selectParties.options.length - 1; i >= 0; i--) { + selectParties.remove(i); + } + + let el = document.createElement("option"); + el.textContent = "Veuillez sélectionner un parti"; + el.value = 0; + selectParties.appendChild(el); + parties.forEach((party) => { + el = document.createElement("option"); + el.textContent = party.Name; + el.value = party.ID; + selectParties.appendChild(el); + }); + } + + async saveCandidateList() { + if (this.method == "POST") + document.getElementById("candidateList-modal-id").value = null; + + let candidateList = await this.CandidateListModel.saveCandidateList( + this.method, + parseInt(document.getElementById("candidateList-modal-id").value), + document.getElementById("candidateList-modal-name").value, + parseInt(document.getElementById("candidateList-modal-party").value), + this.round.ID, + this.area.ID + ); + await this.displayCandidateLists(); + Common.toggleModal("candidateList-modal", "candidateList-modal-card"); + this.activateCandidateList(candidateList); + // TODO open desks + // TODO open candidateLists + return candidateList; + } + async deleteCandidateList(candidateList) { + await this.CandidateListModel.deleteCandidateList(candidateList.ID); + await this.displayCandidateLists(); + // TODO empty desks + // TODO empty candidateLists } } diff --git a/web/components/management/round.js b/web/components/management/round.js index 673fb62..ce7429d 100644 --- a/web/components/management/round.js +++ b/web/components/management/round.js @@ -35,6 +35,7 @@ class Round { <div class="modal" id="round-modal"></div> <div class="modal" id="capturers-modal"></div> + <div class="modal" id="candidateList-modal"></div> `; this.roundsHandler = await RoundsCard.mount("rounds-list", this) this.deskRoundsHandler = await RoundDesks.mount("round-desks", this) diff --git a/web/components/management/rounds-card.js b/web/components/management/rounds-card.js index 06d2b9a..1dfb0e2 100644 --- a/web/components/management/rounds-card.js +++ b/web/components/management/rounds-card.js @@ -44,7 +44,6 @@ class Round { this.mountModal("round-modal"); this.handleDom(); this.displayRounds(); - this.refreshElections(); } handleDom() { @@ -77,7 +76,6 @@ class Round { async refreshElections() { let selectElection = document.getElementById("round-modal-election-id"); let elections = await this.ElectionModel.getElections(); - elections.forEach((election) => {}); for (let i = selectElection.options.length - 1; i >= 0; i--) { selectElection.remove(i); } @@ -173,10 +171,6 @@ class Round { roundHandler.activateRound(round); roundHandler.parent.deskRoundsHandler.round = round; await roundHandler.parent.deskRoundsHandler.displayDesks(); - document - .getElementById("candidate-list-new") - .setAttribute("disabled", "true"); - // TODO open candidate list roundHandler.parent.candidateListHandler.round = round; roundHandler.parent.candidateListHandler.displayAreas(); }); @@ -219,6 +213,9 @@ class Round { } async activateRound(roundToActivate) { + document + .getElementById("candidate-list-new") + .setAttribute("disabled", "true"); let rounds = await this.RoundModel.getRounds(); rounds.forEach((round) => { document @@ -231,16 +228,18 @@ class Round { // TODO unselect candidateLists } - newRound() { + async newRound() { this.method = "POST"; + await this.refreshElections(); document.getElementById("round-modal-id").value = null; document.getElementById("round-modal-date").value = null; document.getElementById("round-modal-round").value = null; Common.toggleModal("round-modal", "round-modal-card"); } - editRound(round) { + async editRound(round) { this.method = "PUT"; + await this.refreshElections(); document.getElementById("round-modal-id").value = round.ID; document.getElementById("round-modal-election-id").value = round.ElectionID; document.getElementById("round-modal-date").value = new Date(round.Date) diff --git a/web/services/model/area-model.js b/web/services/model/area-model.js index d60a9f1..f89bba6 100644 --- a/web/services/model/area-model.js +++ b/web/services/model/area-model.js @@ -75,7 +75,7 @@ class AreaModel { } } - async deleteElection(ID) { + async deleteArea(ID) { try { const response = await fetch("/api/Area/" + ID, { method: "delete", diff --git a/web/services/model/candidateList-model.js b/web/services/model/candidateList-model.js new file mode 100644 index 0000000..53bc774 --- /dev/null +++ b/web/services/model/candidateList-model.js @@ -0,0 +1,100 @@ +import * as Messages from "/services/messages/messages.js"; + +let candidateListModel; + +export function getCandidateListModel() { + if (candidateListModel == null) { + candidateListModel = new CandidateListModel(); + } + return candidateListModel; +} + +class CandidateListModel { + constructor() {} + + async getCandidateList(id) { + if (this.candidateLists == null) await this.refreshCandidateLists(); + let candidateListToGet; + this.candidateLists.forEach((candidateList) => { + if (candidateList.ID == id) candidateListToGet = candidateList; + }); + return candidateListToGet; + } + + async getCandidateLists() { + if (this.candidateLists == null) { + try { + const response = await fetch("/api/CandidateList/", { + method: "GET", + headers: new Headers({ + "XSRF-Token": this.current_user.xsrftoken, + }), + }); + if (response.status !== 200) { + throw new Error( + `CandidateLists could not be fetched (status ${response.status})` + ); + } + this.candidateLists = await response.json(); + } catch (e) { + Messages.Show("is-warning", e.message); + console.error(e); + } + } + return this.candidateLists; + } + + async saveCandidateList(method, ID, Name, PartyID, RoundID, AreaID) { + try { + const response = await fetch("/api/CandidateList/" + ID, { + method: method, + headers: new Headers({ + "XSRF-Token": this.current_user.xsrftoken, + }), + body: JSON.stringify({ + ID: ID, + Name: Name, + PartyID: PartyID, + RoundID: RoundID, + AreaID: AreaID, + }), + }); + if (response.status !== 200) { + throw new Error( + `CandidateList could not be updated or created (status ${response.status})` + ); + } + this.refreshCandidateLists(); + return await response.json(); + } catch (e) { + Messages.Show("is-warning", e.message); + console.error(e); + return; + } + } + + async deleteCandidateList(ID) { + try { + const response = await fetch("/api/CandidateList/" + ID, { + method: "delete", + headers: new Headers({ + "XSRF-Token": this.current_user.xsrftoken, + }), + }); + if (response.status !== 200) { + throw new Error( + `CandidateList could not be deleted (status ${response.status})` + ); + } + } catch (e) { + Messages.Show("is-warning", e.message); + console.error(e); + } + await this.refreshCandidateLists(); + } + + async refreshCandidateLists() { + this.candidateLists = null; + await this.getCandidateLists(); + } +} diff --git a/web/services/model/party-model.js b/web/services/model/party-model.js index 5216213..b90aefd 100644 --- a/web/services/model/party-model.js +++ b/web/services/model/party-model.js @@ -93,6 +93,6 @@ class PartyModel { async refreshParties() { this.parties = null; - this.getParties(); + await this.getParties(); } } -- GitLab