Skip to content
Snippets Groups Projects
Commit 7acde9dc authored by Alexis POYEN's avatar Alexis POYEN
Browse files

Merge branch '76-select-maps-for-elections' into 'master'

Resolve "Select maps for elections"

Closes #76

See merge request !75
parents 1e1d77f9 5931b7c5
No related branches found
No related tags found
1 merge request!75Resolve "Select maps for elections"
Pipeline #6629 passed
...@@ -67,6 +67,10 @@ La démo est accessible avec l'url https://elections.127.0.0.1.nip.io ...@@ -67,6 +67,10 @@ La démo est accessible avec l'url https://elections.127.0.0.1.nip.io
- `./miscellaneous/keycloack` contient un environnement Keycloack qui peut être utilisé pour déployer un environnement OAuth2 - `./miscellaneous/keycloack` contient un environnement Keycloack qui peut être utilisé pour déployer un environnement OAuth2
- `./web` est le répertoire où est stocké l'application front-end en JavaScript natif et avec le framework CSS Bulma publié par le serveur back-end. - `./web` est le répertoire où est stocké l'application front-end en JavaScript natif et avec le framework CSS Bulma publié par le serveur back-end.
Le répertoire `./web/assets/maps` contient les cartes qui peuvent être utilisé dans l'application. Pour en ajouter de nouvelles en production copier les fichiers désirés dans ce répertoire.
Avec docker-compose : docker cp <fichier-à-importer> <nom-du-conteneur>:/app/web/assets/maps/<fichier-à-importer>
### Utilisateurs et droits ### Utilisateurs et droits
**Utilisateurs techniques** **Utilisateurs techniques**
Les utilisateurs techniques permettent de s'authentifier à l'application et d'accéder aux API en fonction du rôle de l'utilisateur qui définit alors ses droits. Les utilisateurs techniques permettent de s'authentifier à l'application et d'accéder aux API en fonction du rôle de l'utilisateur qui définit alors ses droits.
......
...@@ -4,19 +4,20 @@ services: ...@@ -4,19 +4,20 @@ services:
elections-container: elections-container:
image: elections image: elections
build: . build: .
#command: -debug command: -debug
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- ./configs:/app/configs - ./configs:/app/configs
- ./letsencrypt_cache:/app/letsencrypt_cache - ./letsencrypt_cache:/app/letsencrypt_cache
- ./data:/app/data - ./data:/app/data
- maps-volume:/app/web/assets/maps
ports: ports:
- 443:443 - 443:443
- 80:80 - 80:80
environment: environment:
- HOSTNAME=${HOSTNAME} - HOSTNAME=${HOSTNAME}
- ADMIN_ROLE=${ADMIN_ROLE} - ADMIN_ROLE=${ADMIN_GROUP}
- REDIRECT_URL=${REDIRECT_URL} - REDIRECT_URL=${REDIRECT_URL}
- CLIENT_ID=${CLIENT_ID} - CLIENT_ID=${CLIENT_ID}
- CLIENT_SECRET=${CLIENT_SECRET} - CLIENT_SECRET=${CLIENT_SECRET}
...@@ -24,3 +25,6 @@ services: ...@@ -24,3 +25,6 @@ services:
- TOKEN_URL=${TOKEN_URL} - TOKEN_URL=${TOKEN_URL}
- USERINFO_URL=${USERINFO_URL} - USERINFO_URL=${USERINFO_URL}
- LOGOUT_URL=${LOGOUT_URL} - LOGOUT_URL=${LOGOUT_URL}
volumes:
maps-volume:
...@@ -104,6 +104,8 @@ func (d *DataHandler) putElection(w http.ResponseWriter, r *http.Request, id int ...@@ -104,6 +104,8 @@ func (d *DataHandler) putElection(w http.ResponseWriter, r *http.Request, id int
} }
o.Name = election.Name o.Name = election.Name
o.BallotType = election.BallotType o.BallotType = election.BallotType
o.MapAreaFile = election.MapAreaFile
o.MapSectionFile = election.MapSectionFile
d.db.Save(&o) d.db.Save(&o)
json.NewEncoder(w).Encode(o) json.NewEncoder(w).Encode(o)
......
package models package models
import ( import (
"encoding/json"
"fmt"
"net/http" "net/http"
"path/filepath"
"strings" "strings"
"time" "time"
...@@ -46,14 +49,16 @@ const ErrorValidatedVote = "Error the vote have already been validated and can't ...@@ -46,14 +49,16 @@ const ErrorValidatedVote = "Error the vote have already been validated and can't
// Election represent an election divided in areas with 1 or several rounds // Election represent an election divided in areas with 1 or several rounds
type Election struct { type Election struct {
ID uint `gorm:"primary_key"` ID uint `gorm:"primary_key"`
CreatedAt time.Time `json:"-"` CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-"` DeletedAt *time.Time `json:"-"`
Name string Name string
BallotType string BallotType string
Areas []Area MapAreaFile string
Rounds []Round MapSectionFile string
Areas []Area
Rounds []Round
} }
// Area represent an area of an election divided in one or several Sections // Area represent an area of an election divided in one or several Sections
...@@ -251,6 +256,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) { ...@@ -251,6 +256,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) {
d.handleCandidate(w, r) d.handleCandidate(w, r)
case "Vote": case "Vote":
d.handleVote(w, r) d.handleVote(w, r)
case "Maps":
d.handleMaps(w, r)
} }
} }
...@@ -270,3 +277,15 @@ func (d *DataHandler) getLoggedUser(w http.ResponseWriter, r *http.Request) inte ...@@ -270,3 +277,15 @@ func (d *DataHandler) getLoggedUser(w http.ResponseWriter, r *http.Request) inte
return nil return nil
} }
func (d *DataHandler) handleMaps(w http.ResponseWriter, r *http.Request) {
switch method := r.Method; method {
case "GET":
matches, _ := filepath.Glob("web/assets/maps/*")
fmt.Println(matches)
json.NewEncoder(w).Encode(matches)
default:
http.Error(w, "method not allowed", 400)
}
}
...@@ -34,13 +34,13 @@ func AdminTests(t *testing.T) { ...@@ -34,13 +34,13 @@ func AdminTests(t *testing.T) {
do("DELETE", "/api/Capturer/3", xsrfHeader, ``, 200, ``) do("DELETE", "/api/Capturer/3", xsrfHeader, ``, 200, ``)
// Create an Election // Create an Election
do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":null,"Rounds":null}`) do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":null,"Rounds":null}`)
// Get the election // Get the election
do("GET", "/api/Election/1", xsrfHeader, ``, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[],"Rounds":null}`) do("GET", "/api/Election/1", xsrfHeader, ``, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[],"Rounds":null}`)
// Get all the elections // Get all the elections
do("GET", "/api/Election/", xsrfHeader, ``, 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[],"Rounds":null}]`) do("GET", "/api/Election/", xsrfHeader, ``, 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[],"Rounds":null}]`)
// Update an election // Update an election
do("PUT", "/api/Election/1", xsrfHeader, `{"ID":1,"Name":"Grand-Lyon 2020", "BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand-Lyon 2020","BallotType":"metropolitan-direct","Areas":[],"Rounds":null}`) do("PUT", "/api/Election/1", xsrfHeader, `{"ID":1,"Name":"Grand-Lyon 2020", "BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 200, `{"ID":1,"Name":"Grand-Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[],"Rounds":null}`)
// Create an Area // Create an Area
do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`) do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`)
......
...@@ -36,13 +36,13 @@ func CapturerTests(t *testing.T) { ...@@ -36,13 +36,13 @@ func CapturerTests(t *testing.T) {
do("DELETE", "/api/Capturer/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) do("DELETE", "/api/Capturer/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
// Create an election should fail with 405 // Create an election should fail with 405
do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct"}`, 405, `You're not authorize to execute this method on this ressource.`) do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 405, `You're not authorize to execute this method on this ressource.`)
// Get an Election // Get an Election
do("GET", "/api/Election/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}`) do("GET", "/api/Election/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}`)
// Get all the elections // Get all the elections
do("GET", "/api/Election/", xsrfHeader, "", 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}]`) do("GET", "/api/Election/", xsrfHeader, "", 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}]`)
// Update an election should fail with 405 // Update an election should fail with 405
do("PUT", "/api/Election/1", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct"}`, 405, `You're not authorize to execute this method on this ressource.`) do("PUT", "/api/Election/1", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 405, `You're not authorize to execute this method on this ressource.`)
// Delete an election should fail with 405 // Delete an election should fail with 405
do("DELETE", "/api/Election/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) do("DELETE", "/api/Election/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
......
...@@ -292,7 +292,7 @@ func resetDataWithData(t *testing.T) { ...@@ -292,7 +292,7 @@ func resetDataWithData(t *testing.T) {
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`) do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`)
do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`) do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`)
do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":null,"Rounds":null}`) do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":null,"Rounds":null}`)
do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`) do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`)
do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`) do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`)
do("POST", "/api/Desk", xsrfHeader, `{"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587}`, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":null}`) do("POST", "/api/Desk", xsrfHeader, `{"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587}`, 200, `{"ID":1,"SectionID":1,"Name":"Desk 1","WitnessDesk":true,"Subscribed":9587,"DeskRounds":null}`)
......
...@@ -34,13 +34,13 @@ func VisualizerTests(t *testing.T) { ...@@ -34,13 +34,13 @@ func VisualizerTests(t *testing.T) {
do("DELETE", "/api/Capturer/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) do("DELETE", "/api/Capturer/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
// Create an election should fail with 405 // Create an election should fail with 405
do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct"}`, 405, `You're not authorize to execute this method on this ressource.`) do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 405, `You're not authorize to execute this method on this ressource.`)
// Get an Election // Get an Election
do("GET", "/api/Election/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}`) do("GET", "/api/Election/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}`)
// Get all the elections // Get all the elections
do("GET", "/api/Election/", xsrfHeader, "", 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}]`) do("GET", "/api/Election/", xsrfHeader, "", 200, `[{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json","Areas":[{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}],"Rounds":null}]`)
// Update an election should fail with 405 // Update an election should fail with 405
do("PUT", "/api/Election/1", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct"}`, 405, `You're not authorize to execute this method on this ressource.`) do("PUT", "/api/Election/1", xsrfHeader, `{"Name":"Grand Lyon 2020", "BallotType":"metropolitan-direct","MapAreaFile":"web/assets/maps/area.json","MapSectionFile":"web/assets/maps/section.json"}`, 405, `You're not authorize to execute this method on this ressource.`)
// Delete an election should fail with 405 // Delete an election should fail with 405
do("DELETE", "/api/Election/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) do("DELETE", "/api/Election/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
......
...@@ -121,6 +121,24 @@ class Election { ...@@ -121,6 +121,24 @@ class Election {
</select> </select>
</div> </div>
</div> </div>
<div class="field">
<label>Carte de circonscriptions</label><br />
<div class="control select">
<select
name="map-area-selector"
id="election-modal-area-map"
></select>
</div>
</div>
<div class="field">
<label>Carte de villes</label><br />
<div class="control select">
<select
name="map-section-selector"
id="election-modal-section-map"
></select>
</div>
</div>
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button id="election-modal-save" class="button is-success"> <button id="election-modal-save" class="button is-success">
...@@ -302,16 +320,18 @@ class Election { ...@@ -302,16 +320,18 @@ class Election {
.classList.add("active-card"); .classList.add("active-card");
} }
newElection() { async newElection() {
this.method = "POST"; this.method = "POST";
await this.refreshMapSelect();
document.getElementById("election-modal-id").value = null; document.getElementById("election-modal-id").value = null;
document.getElementById("election-modal-name").value = null; document.getElementById("election-modal-name").value = null;
document.getElementById("election-modal-ballot-type").value = null; document.getElementById("election-modal-ballot-type").value = null;
Common.toggleModal("election-modal", "election-modal-card"); Common.toggleModal("election-modal", "election-modal-card");
} }
editElection(election) { async editElection(election) {
this.method = "PUT"; this.method = "PUT";
await this.refreshMapSelect();
document.getElementById("election-modal-id").value = election.ID; document.getElementById("election-modal-id").value = election.ID;
document.getElementById("election-modal-name").value = election.Name; document.getElementById("election-modal-name").value = election.Name;
document.getElementById("election-modal-ballot-type").value = document.getElementById("election-modal-ballot-type").value =
...@@ -328,7 +348,9 @@ class Election { ...@@ -328,7 +348,9 @@ class Election {
this.method, this.method,
parseInt(document.getElementById("election-modal-id").value), parseInt(document.getElementById("election-modal-id").value),
document.getElementById("election-modal-name").value, document.getElementById("election-modal-name").value,
document.getElementById("election-modal-ballot-type").value document.getElementById("election-modal-ballot-type").value,
document.getElementById("election-modal-area-map").value,
document.getElementById("election-modal-section-map").value
); );
await this.displayElections(); await this.displayElections();
...@@ -346,7 +368,6 @@ class Election { ...@@ -346,7 +368,6 @@ class Election {
this.parent.sectionHandler.emptySections(); this.parent.sectionHandler.emptySections();
this.parent.deskHandler.emptyDesks(); this.parent.deskHandler.emptyDesks();
document.getElementById("areas").style.display = "none"; document.getElementById("areas").style.display = "none";
} }
cloneElection(election) { cloneElection(election) {
...@@ -378,4 +399,52 @@ class Election { ...@@ -378,4 +399,52 @@ class Election {
this.parent.areaHandler.election = electionCloned; this.parent.areaHandler.election = electionCloned;
await this.parent.areaHandler.displayAreas(); await this.parent.areaHandler.displayAreas();
} }
async refreshMapSelect() {
let selectMapAreas = document.getElementById("election-modal-area-map");
let selectMapSections = document.getElementById(
"election-modal-section-map"
);
let maps;
try {
const response = await fetch("/api/Maps/", {
method: "GET",
headers: new Headers({
"XSRF-Token": this.ElectionModel.current_user.xsrftoken,
}),
});
if (response.status !== 200) {
throw new Error(
`Maps could not be fetched (status ${response.status})`
);
}
maps = await response.json();
} catch (e) {
Messages.Show("is-warning", e.message);
console.error(e);
}
this.addMapsInSelect(maps, selectMapAreas);
this.addMapsInSelect(maps, selectMapSections);
}
addMapsInSelect(maps, select) {
for (let i = select.options.length - 1; i >= 0; i--) {
select.remove(i);
}
let el = document.createElement("option");
el.textContent = "Veuillez sélectionner une carte";
el.value = 0;
select.appendChild(el);
maps.forEach((map) => {
el = document.createElement("option");
el.textContent = map.substring(
map.lastIndexOf("/") + 1,
map.lastIndexOf(".")
);
el.value = map;
select.appendChild(el);
});
}
} }
...@@ -48,7 +48,7 @@ class RoundDesk { ...@@ -48,7 +48,7 @@ class RoundDesk {
<div id="desk-round-details"></div> <div id="desk-round-details"></div>
</div> </div>
<div class="column is-half"> <div class="column is-half">
<div id="vote-section" class="card"></div> <div id="vote-section" class="card-no-hover"></div>
</div> </div>
</div> </div>
`; `;
......
// Imports // Imports
import * as PartyModel from "/services/model/party-model.js"; import * as PartyModel from "/services/model/party-model.js";
import * as AreaModel from "/services/model/area-model.js"; import * as AreaModel from "/services/model/area-model.js";
import * as ElectionModel from "/services/model/election-model.js";
export async function mount(parent) { export async function mount(parent) {
const mapComponent = new MapComponent(parent); const mapComponent = new MapComponent(parent);
...@@ -12,14 +13,24 @@ class MapComponent { ...@@ -12,14 +13,24 @@ class MapComponent {
this.parent = parent; this.parent = parent;
this.PartyModel = PartyModel.getPartyModel(); this.PartyModel = PartyModel.getPartyModel();
this.AreaModel = AreaModel.getAreaModel(); this.AreaModel = AreaModel.getAreaModel();
this.ElectionModel = ElectionModel.getElectionModel();
} }
async displayMapAreas() { async displayMapAreas() {
await this.initMap("/assets/maps/area.json", this.colorAreas); let election = await this.ElectionModel.getElection(
this.parent.parent.round.ElectionID
);
await this.initMap(
election.MapAreaFile.replace("web/", ""),
this.colorAreas
);
} }
async displayMapSections() { async displayMapSections() {
await this.initMap("/assets/maps/section.json", this.colorSections); let election = await this.ElectionModel.getElection(
this.parent.parent.round.ElectionID
);
await this.initMap(election.MapSectionFile.replace("web/", ""), this.colorSections);
} }
async initMap(mapFile, colorationFunction) { async initMap(mapFile, colorationFunction) {
...@@ -42,7 +53,7 @@ class MapComponent { ...@@ -42,7 +53,7 @@ class MapComponent {
container: "map-component", // container id container: "map-component", // container id
style: "/assets/mapbox/vector.json", // stylesheet location style: "/assets/mapbox/vector.json", // stylesheet location
center: [4.9, 45.75], // starting position [lng, lat] center: [4.9, 45.75], // starting position [lng, lat]
zoom: 9.7, // starting zoom zoom: 9.5, // starting zoom
}); });
this.map.on("load", () => { this.map.on("load", () => {
......
...@@ -16,7 +16,7 @@ class VotePage { ...@@ -16,7 +16,7 @@ class VotePage {
const mountpoint = where; const mountpoint = where;
document.getElementById(mountpoint).innerHTML = /* HTML */ ` document.getElementById(mountpoint).innerHTML = /* HTML */ `
<section style="margin-bottom: 230px;"> <section style="margin-bottom: 230px;">
<div class="container"><div id="vote-section" class="card"></div></div> <div class="container"><div id="vote-section" class="card-no-hover"></div></div>
</section> </section>
`; `;
this.deskRoundHandler = await DeskRound.mount("vote-section", this); this.deskRoundHandler = await DeskRound.mount("vote-section", this);
......
import * as Messages from "/services/messages/messages.js"; import * as Messages from "/services/messages/messages.js";
let electionModel let electionModel;
export function getElectionModel(){ export function getElectionModel() {
if(electionModel == null) { if (electionModel == null) {
electionModel = new ElectionModel(); electionModel = new ElectionModel();
} }
return electionModel return electionModel;
} }
class ElectionModel { class ElectionModel {
...@@ -44,7 +44,14 @@ class ElectionModel { ...@@ -44,7 +44,14 @@ class ElectionModel {
return this.elections; return this.elections;
} }
async saveElection(method, ID, Name, BallotType) { async saveElection(
method,
ID,
Name,
BallotType,
MapAreaFile,
MapSectionFile
) {
try { try {
const response = await fetch("/api/Election/" + ID, { const response = await fetch("/api/Election/" + ID, {
method: method, method: method,
...@@ -55,6 +62,8 @@ class ElectionModel { ...@@ -55,6 +62,8 @@ class ElectionModel {
ID: ID, ID: ID,
Name: Name, Name: Name,
BallotType: BallotType, BallotType: BallotType,
MapAreaFile: MapAreaFile,
MapSectionFile: MapSectionFile,
}), }),
}); });
if (response.status == 409) { if (response.status == 409) {
......
...@@ -214,7 +214,7 @@ select { ...@@ -214,7 +214,7 @@ select {
} }
#map-section { #map-section {
height: 70vh; height: 65vh;
} }
#map-component { #map-component {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment