Client-side poll creation

v1.2.0
aymm 2021-04-04 16:59:09 +02:00
parent 44d0940e23
commit 4572e730f9
Signed by: phlaym
GPG Key ID: A06651BAB6777237
4 changed files with 155 additions and 3 deletions

View File

@ -2,5 +2,9 @@
require_once __DIR__ .'/bootstrap.php';
// Support the old Dragonpolls links
if (isset($_GET['poll'])) {
redirect('view_poll.php?id='.$_GET['poll']);
}
echo get_page_header();
echo get_page_footer();

View File

@ -11,11 +11,43 @@ use APnutI\Entities\Poll;
use APnutI\Entities\User;
try {
echo get_page_header('New Poll', true, ['poll']);
echo get_page_header('New Poll', true, ['new_poll']);
} catch (\Exception $e) {
die('Something went wrong :( "'.$e->getMessage().'"');
die('Something went wrong :( "'.$e->getMessage().'"' . get_page_footer());
}
if (!$api->isAuthenticated(false, true)) {
die('You need to be logged in to create a new poll!');
die('You need to be logged in to create a new poll!' . get_page_footer());
}
?>
<form method="POST" class="create-poll">
<label for="prompt">Prompt</label>
<input type="text" name="prompt" placeholder="What would you like to poll about?" id="prompt" required/>
<label for="options">Options</label>
<div id="options">
<?php
for ($i = 0; $i < 10; $i++) { ?>
<input
type="text"
name="option[]"
placeholder="This will be option #<?= $i+1 ?>" <?= $i < 2 ? 'required' : '' ?>/>
<?php } ?>
</div>
<label for="anonymous">Anonymous</label>
<input type="checkbox" name="anonymous" id="anonymous" />
<label for="public">Public</label>
<input type="checkbox" name="public" id="public" />
<label for="max_options">Max Options</label>
<input type="number" name="max_options" id="max_options" min="1" max="10" value="1" required/>
<label for="duration">Duration</label>
<div id="duration">
<input type="number" name="duration_days" value="1" min="0" required/><span>day(s)</span>
<input type="number" name="duration_hours" value="0" min="0" required/><span>hour(s)</span>
<input type="number" name="duration_minutes" value="0" min="0" required/><span>minute(s)</span><br>
<span id="openUntil"></span>
</div>
<span class="error"></span>
<button type="submit" name="submit" value="submit">Create poll</button>
</form>
</form>

52
scripts/new_poll.js Normal file
View File

@ -0,0 +1,52 @@
window.addEventListener('DOMContentLoaded', () => {
for (const el of document.querySelectorAll('.create-poll input')) {
el.onchange = validatePoll;
}
});
function validatePoll() {
let errors = [];
const form = document.querySelector('.create-poll');
if (!form.querySelector('input[name=prompt]').value) {
errors.push('Prompt cannot be empty');
}
const numOptionsProvided = Array.from(form.querySelectorAll('input[name="option[]"]'))
.map(x => !!x.value)
.filter(x => x)
.length;
if (numOptionsProvided <= 1) {
errors.push('At least two options must be provided');
}
const maxOptions = parseInt(form.querySelector('input[name=max_options]').value);
if (numOptionsProvided > 1 && (isNaN(maxOptions) || maxOptions <= 0 || maxOptions >= numOptionsProvided)) {
errors.push(`Max Options needs to be greater than 0 and smaller than ${numOptionsProvided}`);
}
const durationDays = parseInt(form.querySelector('input[name=duration_days]').value);
const durationHours = parseInt(form.querySelector('input[name=duration_hours]').value);
const durationMinutes = parseInt(form.querySelector('input[name=duration_minutes]').value);
const durationTotalMinutes = durationDays*60*24 + durationHours * 60 + durationMinutes;
// 20160 is the max duration accepted by pnut
if (isNaN(durationTotalMinutes) || durationTotalMinutes < 1 || durationTotalMinutes > 20160) {
errors.push('Duration must be more than 1 and less than 20160 minutes');
}
const closesAtLabel = document.getElementById('openUntil');
if (!isNaN(durationTotalMinutes)) {
// Add duration_total_minutes to the curent Date
const closes_at = new Date(new Date().getTime() + durationTotalMinutes*60000);
closesAtLabel.innerText = `Open until ${closes_at.toLocaleString()}`;
} else {
closesAtLabel.innerText = '';
}
const errorSpan = form.querySelector('.error');
if (errors.length) {
errorSpan.innerHTML = `<ul><li>${errors.join('</li><li>')}</li></ul>`;
} else {
errorSpan.innerText = '';
}
form.querySelector('button[name=submit]').disabled = !!errors.length;
}

View File

@ -182,6 +182,70 @@ datewrapper time {
font-size: x-large;
}
/* Poll creation */
.create-poll {
display: grid;
max-width: 400px;
grid-auto-columns: auto 1fr;
grid-column-gap: 8px;
}
.create-poll input[type=text],
.create-poll input[type=number],
.create-poll input[type=checkbox] {
grid-column: 2;
}
.create-poll input[type=number] {
max-width: 3em;
background-color: var(--secondary-bg-color);
color: var(--main-accent-color);
}
.create-poll input[type=text] {
display: block;
width: 100%;
background-color: var(--secondary-bg-color);
color: var(--main-accent-color);
}
.create-poll input[type=text]:focus {
outline: 1px solid var(--main-accent-color);
}
.create-poll label {
grid-column: 1;
}
.create-poll label[for=options] {
grid-row: 2;
}
.create-poll #options {
grid-row: 2;
}
.create-poll label[for=anonymous] {
grid-row: 3
}
.create-poll label[for=public] {
grid-row: 4
}
.create-poll label[for=max_options] {
grid-row: 5
}
.create-poll label[for=duration] {
grid-row: 6
}
#duration {
grid-row: 6;
}
.create-poll button[type=submit] {
grid-row: 8;
}
.create-poll .error {
grid-column: 1/3;
grid-row: 7;
background-color: rgba(255, 0, 0, 0.3);
}
.create-poll .error:not(:empty) {
margin: 8px;
}
/* Footer */
footer {
display: flex;