This commit is contained in:
louiscklaw
2025-01-31 19:15:17 +08:00
parent 09adae8c8e
commit 6c60a73f30
1546 changed files with 286918 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
body {
font-family: 'Calibri';
position: relative;
}
.poll-container {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.poll-frame {
display: flex;
justify-content: center;
align-items: flex-start;
margin: 20px 0;
flex-wrap: wrap;
}
.option-container {
width: 200px;
height: 200px;
background-color: #f9f9da;
border-radius: 20px;
margin: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-wrap: wrap;
cursor: pointer;
}
.option-container:hover {
opacity: 0.7;
}
.title {
font-size: 30px;
font-weight: bold;
}
.address {
font-size: 20px;
}
#poll-submit-button,
#poll-terminate-button,
#poll-terminate-confirm-button {
font-size: 20px;
font-family: 'Lato', sans-serif;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
min-width: 110px;
}
#poll-submit-button {
box-shadow: 0px 1px 12px #f2d492;
background-color: #f2d492;
border: none;
color: #293241;
}
#poll-terminate-button,
#poll-terminate-confirm-button {
box-shadow: none;
background-color: #d8684e;
border: #c7492c 3px solid;
color: white;
font-weight: bold;
}
.poll-title {
font-size: 20px;
margin-top: 20px;
font-weight: bold;
}
.selected {
border: solid black 5px;
}
.vote {
font-size: 15px;
}
.warning-sign {
font-size: 70px;
width: 90px;
height: 90px;
border-radius: 200px;
border: solid 4px black;
display: flex;
justify-content: center;
align-items: center;
}
.warning {
font-size: 30px;
font-weight: bold;
}
.reminder-text {
text-align: center;
}
.modal-title {
font-size: 25px;
font-weight: bold;
color: #293241;
font-family: 'Lato', sans-serif;
transform: translateX(10px);
}
.modal-header {
display: flex;
justify-content: space-between;
}
.modal-body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.modal-footer {
display: flex;
justify-content: center;
align-content: center;
}
#back-page {
position: absolute;
left: 50px;
top: 15px;
text-decoration: none;
}
.bi-chevron-left {
font-size: 30px;
top: 90px;
left: 40px;
color: #444a58;
cursor: pointer;
}

View File

@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="./datetimePoll.css" />
<title>Venue Poll</title>
</head>
<body style="display: none">
<div class="navbar-container container">
<!-- NAVBAR: to be loaded with js -->
</div>
<div class="poll-container">
<div class="poll-title"></div>
<div class="poll-frame"></div>
<div class="button-container"></div>
<!-- Back Button -->
<a id="back-page">
<i class="bi bi-chevron-left"></i>
</a>
</div>
<!-- Modal -->
<!-- Terminate Poll Confirmation Modal -->
<div
class="modal fade"
id="delete-poll-modal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div></div>
<h5 class="modal-title">Terminate Poll</h5>
<div class="exit-modal">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="warning-sign">
<i class="fa-solid fa-exclamation"></i>
</div>
<div class="warning">Are you sure?</div>
<div class="reminder-text">You will not be able to revert this action!</div>
<div class="modal-footer">
<button id="poll-terminate-confirm-button" type="submit" class="modal-submit-button">
TERMINATE
</button>
</div>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
crossorigin="anonymous"
></script>
<script type="module" src="./datetimePoll.js"></script>
</body>
</html>

View File

@@ -0,0 +1,154 @@
import { addNavbar } from '/functions/addNavbar.js';
import { loadName } from '/functions/loadName.js';
window.addEventListener('load', async () => {
addNavbar();
await loadName();
await loadOptions();
document.body.style.display = 'block';
});
async function loadOptions() {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const res = await fetch(`/events/poll/datetime/${eventId}`);
const result = await res.json();
if (result.status) {
let pollTitle = '';
let pollFrameHTML = '';
let buttonContainerHTML = '';
// Poll title HTML
if (result.pollTerminated) {
pollTitle = 'Poll Terminated';
} else if (result.eventDeleted) {
pollTitle = 'Deleted Event';
} else if (!result.creator) {
if (result.choice) {
pollTitle = `Your choice was: <br>
start: ${new Date(result.choice.start)
.toLocaleString('en-US', { hour12: false })
.replace(',', '')
.slice(0, -3)}<br>
end: ${new Date(result.choice.end)
.toLocaleString('en-US', { hour12: false })
.replace(',', '')
.slice(0, -3)}
`;
} else {
pollTitle = 'Please click on the venue option to vote:';
}
} else {
pollTitle = 'You may click button below to terminate poll.';
}
// Poll Options HTML
const optionsList = result.pollOptions;
optionsList.forEach((each, index) => {
const voteCount = result.voteCounts[each.id].count;
pollFrameHTML += `
<div class="option-container" id="option_${each.id}">
<div class="title">
Datetime ${index + 1}
</div>
<div class="start">
Start: ${new Date(each.start_datetime)
.toLocaleString('en-US', { hour12: false })
.replace(',', '')
.slice(0, -3)}
</div>
<div class="end">
End: ${new Date(each.end_datetime)
.toLocaleString('en-US', { hour12: false })
.replace(',', '')
.slice(0, -3)}
</div>
<div class="vote">
${voteCount === '1' ? `${voteCount} Vote` : `${voteCount} Votes`}
</div>
</div>
`;
});
// Button HTML
if (!result.pollTerminated && !result.eventDeleted) {
if (result.creator) {
buttonContainerHTML = `<button id="poll-terminate-button">Terminate Poll</button>`;
} else {
if (!result.choice) {
buttonContainerHTML = `<button id="poll-submit-button">Submit Choice</button>`;
}
}
}
// Add HTML to the page
document.querySelector('.poll-title').innerHTML = pollTitle;
document.querySelector('.poll-frame').innerHTML = pollFrameHTML;
document.querySelector('.button-container').innerHTML = buttonContainerHTML;
// Check if participant that has not yet voted
if (!result.pollTerminated && !result.eventDeleted) {
if (!result.creator && !result.choice) {
// Listen option choice
let optionId;
const optionsDiv = document.querySelectorAll('.option-container');
optionsDiv.forEach((each) => {
each.addEventListener('click', (e) => {
if (e.target.classList.contains('option-container')) {
e.target.classList.add('selected');
optionId = e.target.id;
const otherDiv = document.querySelectorAll(`.option-container:not([id*="${optionId}"])`);
otherDiv.forEach((each) => {
each.classList.remove('selected');
});
}
});
});
// Listen submit button for voting
document.querySelector('#poll-submit-button').addEventListener('click', async () => {
const optionId = document.querySelector('.selected').id.replace('option_', '');
const res = await fetch(`/events/poll/datetime/vote/${eventId}/${optionId}`, {
method: 'POST'
});
const result = await res.json();
if (result.status) {
alert('Successfully voted!');
await loadOptions();
} else {
alert('Unable to submit vote!');
}
});
} else if (result.creator) {
// Listen to terminate button
document.querySelector('#poll-terminate-button').addEventListener('click', () => {
const dateTimeTerminatePoll = new bootstrap.Modal(document.getElementById('delete-poll-modal'));
dateTimeTerminatePoll.show();
document.querySelector('#poll-terminate-confirm-button').addEventListener('click', async () => {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const res = await fetch(`/events/poll/datetime/${eventId}`, {
method: 'DELETE'
});
const result = await res.json();
if (result.status) {
alert('Successfully terminated poll!');
dateTimeTerminatePoll.hide();
loadOptions();
} else {
alert('Unable to terminate poll!');
}
});
});
}
}
// Add backward button
document.querySelector('#back-page').href = `/eventSummary/event.html?${params}`;
} else {
alert('Unable to load datetime poll page!');
window.location.href = '/index.html';
}
}

View File

@@ -0,0 +1,149 @@
body {
font-family: 'Calibri';
position: relative;
}
.poll-container {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.poll-frame {
display: flex;
justify-content: center;
align-items: flex-start;
margin: 20px 0;
flex-wrap: wrap;
}
.option-container {
width: 200px;
height: 200px;
background-color: #f9f9da;
border-radius: 20px;
margin: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-wrap: wrap;
cursor: pointer;
}
.option-container:hover {
opacity: 0.7;
}
.title {
font-size: 30px;
font-weight: bold;
}
.address {
font-size: 20px;
}
#poll-submit-button,
#poll-terminate-button,
#poll-terminate-confirm-button {
font-size: 20px;
font-family: 'Lato', sans-serif;
padding: 10px 20px 10px 20px;
border-radius: 20px;
margin: 18px;
min-width: 110px;
}
#poll-submit-button {
box-shadow: 0px 1px 12px #f2d492;
background-color: #f2d492;
border: none;
color: #293241;
}
#poll-terminate-button,
#poll-terminate-confirm-button {
box-shadow: none;
background-color: #d8684e;
border: #c7492c 3px solid;
color: white;
font-weight: bold;
}
.poll-title {
font-size: 20px;
margin-top: 20px;
font-weight: bold;
}
.selected {
border: solid black 5px;
}
.vote {
font-size: 15px;
}
.warning-sign {
font-size: 70px;
width: 90px;
height: 90px;
border-radius: 200px;
border: solid 4px black;
display: flex;
justify-content: center;
align-items: center;
}
.warning {
font-size: 30px;
font-weight: bold;
}
.reminder-text {
text-align: center;
}
.modal-title {
font-size: 25px;
font-weight: bold;
color: #293241;
font-family: 'Lato', sans-serif;
transform: translateX(10px);
}
.modal-header {
display: flex;
justify-content: space-between;
}
.modal-body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.modal-footer {
display: flex;
justify-content: center;
align-content: center;
}
#back-page {
position: absolute;
left: 50px;
top: 15px;
text-decoration: none;
}
.bi-chevron-left {
font-size: 30px;
top: 90px;
left: 40px;
color: #444a58;
cursor: pointer;
}

View File

@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="./venuePoll.css" />
<title>Venue Poll</title>
</head>
<body style="display: none">
<div class="navbar-container container">
<!-- NAVBAR: to be loaded with js -->
</div>
<div class="poll-container">
<div class="poll-title"></div>
<div class="poll-frame"></div>
<div class="button-container"></div>
<!-- Back Button -->
<a id="back-page">
<i class="bi bi-chevron-left"></i>
</a>
</div>
<!-- Modal -->
<!-- Terminate Poll Confirmation Modal -->
<div
class="modal fade"
id="delete-poll-modal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div></div>
<h5 class="modal-title">Terminate Poll</h5>
<div class="exit-modal">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="warning-sign">
<i class="fa-solid fa-exclamation"></i>
</div>
<div class="warning">Are you sure?</div>
<div class="reminder-text">You will not be able to revert this action!</div>
<div class="modal-footer">
<button id="poll-terminate-confirm-button" type="submit" class="modal-submit-button">
TERMINATE
</button>
</div>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
crossorigin="anonymous"
></script>
<script type="module" src="./venuePoll.js"></script>
</body>
</html>

View File

@@ -0,0 +1,134 @@
import { addNavbar } from '/functions/addNavbar.js';
import { loadName } from '/functions/loadName.js';
window.addEventListener('load', async () => {
addNavbar();
await loadName();
await loadOptions();
document.body.style.display = 'block';
});
async function loadOptions() {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const res = await fetch(`/events/poll/venue/${eventId}`);
const result = await res.json();
if (result.status) {
let pollTitle = '';
let pollFrameHTML = '';
let buttonContainerHTML = '';
// Poll title HTML
if (result.pollTerminated) {
pollTitle = 'Poll Terminated';
} else if (result.eventDeleted) {
pollTitle = 'Deleted Event';
} else if (!result.creator) {
if (result.choice) {
pollTitle = `Your choice was: ${result.choice.address}`;
} else {
pollTitle = 'Please click on the venue option to vote:';
}
} else {
pollTitle = 'You may click button below to terminate poll.';
}
// Poll Options HTML
const optionsList = result.pollOptions;
optionsList.forEach((each, index) => {
const voteCount = result.voteCounts[each.id].count;
pollFrameHTML += `
<div class="option-container" id="option_${each.id}">
<div class="title">
Venue ${index + 1}
</div>
<div class="address">
${each.address}
</div>
<div class="vote">
${voteCount === '1' ? `${voteCount} Vote` : `${voteCount} Votes`}
</div>
</div>
`;
});
// Button HTML
if (!result.pollTerminated && !result.eventDeleted) {
if (result.creator) {
buttonContainerHTML = `<button id="poll-terminate-button">Terminate Poll</button>`;
} else {
if (!result.choice) {
buttonContainerHTML = `<button id="poll-submit-button">Submit Choice</button>`;
}
}
}
// Add HTML to the page
document.querySelector('.poll-title').innerHTML = pollTitle;
document.querySelector('.poll-frame').innerHTML = pollFrameHTML;
document.querySelector('.button-container').innerHTML = buttonContainerHTML;
// Check if participant that has not yet voted
if (!result.pollTerminated && !result.eventDeleted) {
if (!result.creator && !result.choice) {
// Listen option choice
let optionId;
const optionsDiv = document.querySelectorAll('.option-container');
optionsDiv.forEach((each) => {
each.addEventListener('click', (e) => {
e.currentTarget.classList.add('selected');
optionId = e.currentTarget.id;
const otherDiv = document.querySelectorAll(`.option-container:not([id*="${optionId}"])`);
otherDiv.forEach((each) => {
each.classList.remove('selected');
});
});
});
// Listen submit button for voting
document.querySelector('#poll-submit-button').addEventListener('click', async () => {
const optionId = document.querySelector('.selected').id.replace('option_', '');
const res = await fetch(`/events/poll/venue/vote/${eventId}/${optionId}`, {
method: 'POST'
});
const result = await res.json();
if (result.status) {
alert('Successfully voted!');
await loadOptions();
} else {
alert('Unable to submit vote!');
}
});
} else if (result.creator) {
// Listen to terminate button
document.querySelector('#poll-terminate-button').addEventListener('click', () => {
const venueTerminatePoll = new bootstrap.Modal(document.getElementById('delete-poll-modal'));
venueTerminatePoll.show();
document.querySelector('#poll-terminate-confirm-button').addEventListener('click', async () => {
const params = new URLSearchParams(window.location.search);
const eventId = params.get('event-id');
const res = await fetch(`/events/poll/venue/${eventId}`, {
method: 'DELETE'
});
const result = await res.json();
if (result.status) {
alert('Successfully terminated poll!');
venueTerminatePoll.hide();
loadOptions();
} else {
alert('Unable to terminate poll!');
}
});
});
}
}
// Add backward button
document.querySelector('#back-page').href = `/eventSummary/event.html?${params}`;
} else {
alert('Unable to load venue poll page!');
window.location.href = '/index.html';
}
}