diff --git a/web/components/management/candidate-lists.js b/web/components/management/candidate-lists.js index 5c9bab1fff66144fa77d7a78b56ed4f25bc84f30..c58460cb179f7a0c2dc13a09dd6a66ef59a9cd62 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 673fb621a88a28d72c336780b051ec61c1592031..ce7429d06880102d7a7fd7898a641341151f12a9 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 06d2b9aad2ab8751610d3e456a1b35fe5f15b13f..1dfb0e20b6431230efedf52b0b37bd7e87a95397 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 d60a9f15b5a8686a8c18d9fc238000939d852c85..f89bba66312004b79d32b7b90326b68d099f1d32 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 0000000000000000000000000000000000000000..53bc7742778250387885c3911ba931d06f190be9 --- /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 521621355ca2d54613fe6b6a282da91cdeeda0a7..b90aefd63f87e55737ee1bead28d872e85c5477f 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(); } }