diff --git a/internal/models/party.go b/internal/models/party.go index 9036b57797c69eb6b68963c41105d26af9cad665..f5bc7bc80b1c366ce6d68ff4dad8f51efb27b2ca 100644 --- a/internal/models/party.go +++ b/internal/models/party.go @@ -108,6 +108,10 @@ func (d *DataHandler) deleteParty(w http.ResponseWriter, r *http.Request, id int return } + for _, candidateList := range o.CandidateLists { + d.deleteCandidateList(w, r, int(candidateList.ID)) + } + d.db.Delete(&o) } else { http.Error(w, ErrorIDIsMissing, http.StatusNotFound) diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index bb239f74b4b9591f9cae30829f00402831f06e78..ec7887127284eafd50808ad2f319a75f16950654 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -157,9 +157,12 @@ func deletionInCascadePartyTest(t *testing.T) { response := do("GET", "/api/common/WhoAmI", noH, "", 200, "") token := auth.TokenData{} json.Unmarshal([]byte(response), &token) - // xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken} + xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken} - // TODO check that Candidate and candidateLists are deleted in cascade on Party deletion. + // Check that Candidate and candidateLists are deleted in cascade on Party deletion. + do("DELETE", "/api/Party/1", xsrfHeader, ``, 200, ``) + do("GET", "/api/CandidateList/1", xsrfHeader, ``, 404, `id is missing`) + do("GET", "/api/Candidate/1", xsrfHeader, ``, 404, `id is missing`) } // Do an OAuth2 login with an known admin diff --git a/web/components/management/party.js b/web/components/management/party.js index 961e71e1fe53824e2b318b1c5afe2e1610d054c3..791c3e38efff6e03516442159d27661231bdb276 100644 --- a/web/components/management/party.js +++ b/web/components/management/party.js @@ -2,7 +2,10 @@ import * as Auth from "/services/auth/auth.js"; import * as Common from "/services/common/common.js"; import * as PartyModel from "/services/model/party-model.js"; -import { Delete } from "/services/common/delete.js"; +import * as CandidateListModel from "/services/model/candidateList-model.js"; +import * as ElectionModel from "/services/model/election-model.js"; +import * as RoundModel from "/services/model/round-model.js"; +import { AnimateCSS } from "/services/common/common.js"; // DOM elements export async function mount(where) { @@ -13,10 +16,16 @@ export async function mount(where) { class Party { constructor() { this.PartyModel = PartyModel.getPartyModel(); + this.CandidateListModel = CandidateListModel.getCandidateListModel(); + this.ElectionModel = ElectionModel.getElectionModel(); + this.RoundModel = RoundModel.getRoundModel(); } async mount(where) { this.PartyModel.current_user = await Auth.GetUser(); + this.CandidateListModel.current_user = await Auth.GetUser(); + this.ElectionModel.current_user = await Auth.GetUser(); + this.RoundModel.current_user = await Auth.GetUser(); const mountpoint = where; document.getElementById(mountpoint).innerHTML = /* HTML */ ` <div class="container" @@ -115,7 +124,8 @@ class Party { <div class="content"> <nav class="level"> <div class="level-left" style="color:${party.Color}"> - ${party.Name}   <p style="background-color:${party.Color}">Couleur</p> + ${party.Name}    + <p style="background-color:${party.Color}">Couleur</p> </div> <div class="level-right"> <a @@ -157,9 +167,77 @@ class Party { }); document .getElementById(`parties-party-delete-${party.ID}`) - .addEventListener("click", function () { - new Delete(() => { - partyHandler.deleteParty(party); + .addEventListener("click", async function () { + let deleteModal = document.createElement("div"); + deleteModal.classList.add( + "modal", + "animated", + "fadeIn", + "faster", + "is-active" + ); + deleteModal.innerHTML = /* HTML */ ` + <div class="modal-background"></div> + <div class="modal-content"> + <div class="box" style="margin: 2rem;"> + <div class="content"> + <label class="label" + >Les listes suivantes seront supprimées :</label + > + <ul id="candidate-lists-list"></ul> + </div> + <div class="field is-grouped"> + <div class="control"> + <button id="delete-ok" class="button is-danger"> + <span class="icon"><i class="fas fa-check"></i></span + ><span>Supprimer</span> + </button> + </div> + <div class="control"> + <button id="delete-cancel" class="button"> + <span class="icon" + ><i class="fas fa-times-circle"></i></span + ><span>Annuler</span> + </button> + </div> + </div> + </div> + </div> + `; + deleteModal + .querySelector("#" + "delete-ok") + .addEventListener("click", async () => { + await partyHandler.deleteParty(party); + AnimateCSS(deleteModal, "fadeOut", function () { + deleteModal.parentNode.removeChild(deleteModal); + }); + }); + deleteModal + .querySelector("#" + "delete-cancel") + .addEventListener("click", () => { + AnimateCSS(deleteModal, "fadeOut", function () { + deleteModal.parentNode.removeChild(deleteModal); + }); + }); + document.body.appendChild(deleteModal); + partyHandler.party = party; + let candidateLists = await partyHandler.CandidateListModel.getCandidateLists(); + candidateLists.filter(function (candidateList) { + return candidateList.PartyID == partyHandler.party.ID; + }); + candidateLists.forEach(async (candidateList) => { + let round = await partyHandler.RoundModel.getRound(candidateList.RoundID); + let election = await partyHandler.ElectionModel.getElection( + round.ElectionID + ); + let el = document.createElement("li"); + el.innerHTML = + candidateList.Name + + " (" + + election.Name + + " " + + new Date(round.Date).toLocaleDateString() + ")"; + document.getElementById("candidate-lists-list").appendChild(el); }); }); }); @@ -177,8 +255,7 @@ class Party { this.method = "PUT"; document.getElementById("party-modal-id").value = party.ID; document.getElementById("party-modal-name").value = party.Name; - document.getElementById("party-modal-color").value = - party.Color; + document.getElementById("party-modal-color").value = party.Color; Common.toggleModal("party-modal", "party-modal-card"); }