Files
qv/ui/create-election.html

173 lines
6.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create New Election</title>
<link rel="stylesheet" href="/static/css/styles.css">
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app" class="container">
<main>
<h1>Create New Election</h1>
<form @submit.prevent="createElection" class="form">
<div class="form-group">
<label for="name">Election Name</label>
<input type="text" id="name" v-model="election.name" required>
</div>
<div class="form-row">
<div class="form-group">
<label for="tokens">Tokens per Voter</label>
<input type="number" id="tokens" v-model.number="election.tokens" min="1" required>
</div>
<div class="form-group">
<label for="areVotersKnown">Voter Access</label>
<select id="areVotersKnown" v-model="election.areVotersKnown" required>
<option :value="true">Known voters only</option>
<option :value="false">Open to anyone</option>
</select>
<small>Known voters only = codes will be generated and you must give those to your voters</small>
</div>
</div>
<div class="form-group">
<label for="maxVoters">Maximum Number of Voters</label>
<input type="number" id="maxVoters" v-model.number="election.maxVoters" min="1">
<small>0 = unlimited</small>
<span v-if="election.areVotersKnown && election.maxVoters <= 0" class="error-text">Maximum number of voters must be greater than 0 if voters are known</span>
</div>
<div class="form-group">
<label for="expiresAt">Expiration Date</label>
<input type="datetime-local" id="expiresAt" v-model="election.expiresAt" required>
</div>
<div class="form-group">
<label>Choices</label>
<div id="choices-container">
<div v-for="(choice, index) in election.choices" :key="index" class="choice-input">
<input type="text" v-model="election.choices[index]" required>
<button type="button" class="remove-choice" @click="removeChoice(index)" v-show="election.choices.length > 2">×</button>
</div>
</div>
<button type="button" id="add-choice" @click="addChoice">Add Another Choice</button>
</div>
<div class="form-actions">
<button type="submit">Create Election</button>
</div>
</form>
<div v-if="createdElectionId > 0" class="election-info">
<h2>Election Created Successfully</h2>
<div class="info-container">
<span class="info-label">Election ID:</span>
<span class="info-value">{{ createdElectionId }}</span>
<button @click="copyElectionId" class="copy-btn">
Copy
</button>
</div>
</div>
<div v-if="voterIdentities.length > 0" class="voter-codes">
<h2>Voter Access Codes</h2>
<div class="codes-container">
<button @click="copyAllCodes" class="copy-all-btn">
Copy All Codes
</button>
<div class="codes-list">
<div v-for="(code, index) in voterIdentities"
:key="code"
class="code-item">
<span class="code-number">{{ index + 1 }}.</span>
<span class="code-text">{{ code }}</span>
<button @click="copyCode(code)" class="copy-btn">
Copy
</button>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
const app = Vue.createApp({
data() {
return {
election: {
name: "",
tokens: 100,
areVotersKnown: true,
maxVoters: 0,
expiresAt: "",
choices: ["", ""] // Start with two empty choices
},
createdElectionId: 0,
voterIdentities: []
};
},
methods: {
addChoice() {
this.election.choices.push(""); // Add a new empty choice
},
removeChoice(index) {
this.election.choices.splice(index, 1); // Remove choice by index
},
async copyCode(code) {
try {
await navigator.clipboard.writeText(code);
// Optional: Add visual feedback that copy succeeded
} catch (err) {
console.error('Failed to copy code:', err);
}
},
async copyAllCodes() {
try {
const allCodes = this.voterIdentities.join('\n');
await navigator.clipboard.writeText(allCodes);
// Optional: Add visual feedback that copy succeeded
} catch (err) {
console.error('Failed to copy codes:', err);
}
},
createElection() {
this.voterIdentities = [];
const payload = {
...this.election,
expiresAt: this.election.expiresAt + ":00Z", // Add timezone if necessary
choices: this.election.choices.filter(choice => choice.trim() !== "") // Filter out empty choices
};
fetch("/election", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response => {
const locationHeader = response.headers.get('Location');
this.createdElectionId = locationHeader.replace('/election/', '');
return response.json();
})
.then(data => {
this.voterIdentities = data.voterIdentities;
})
.catch(error => {
alert("Failed to create election.");
});
}
}
});
app.mount("#app");
</script>
</body>
</html>