megacommit
This commit is contained in:
parent
2451ab45cb
commit
34ed81516b
51 changed files with 1200 additions and 251 deletions
|
@ -130,6 +130,19 @@ a.button:hover {
|
|||
color: var(--secondary-color-text);
|
||||
}
|
||||
|
||||
button.button:active,
|
||||
a.button:active {
|
||||
background: var(--primary-color);
|
||||
color: var(--primary-color-text);
|
||||
}
|
||||
|
||||
button.button:disabled,
|
||||
a.button:disabled {
|
||||
background: var(--inactive-item-bg);
|
||||
color: var(--dim-text);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
|
@ -175,3 +188,14 @@ tr {
|
|||
.mono {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
input.text {
|
||||
/* generic input adjustments */
|
||||
background: var(--second-bg);
|
||||
color: var(--default-text);
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
|
|
@ -68,9 +68,16 @@ main {
|
|||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
padding: 1rem;
|
||||
max-width: calc(100% - 2rem);
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
#movies a .details span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#movies a:hover .details {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
@ -46,22 +46,30 @@ header div a {
|
|||
color: var(--default-text);
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
#sidebar a:hover {
|
||||
filter: brightness(1.5);
|
||||
background: var(--second-bg);
|
||||
color: var(--default-text);
|
||||
|
||||
}
|
||||
|
||||
#sidebar a.active {
|
||||
background: var(--highlight-bg);
|
||||
border-left: 0.5rem solid var(--highlight-bg);
|
||||
background-color: var(--default-bg);
|
||||
color: var(--highlight-text);
|
||||
}
|
||||
|
||||
#sidebar a.child {
|
||||
padding-left: 2rem;
|
||||
margin-top: 0;
|
||||
/* nice visual*/
|
||||
border-left: 1rem solid var(--second-bg);
|
||||
/* nice visual*/
|
||||
border-left: 0.5rem solid var(--second-bg);
|
||||
}
|
||||
|
||||
#sidebar a.child.active {
|
||||
border-left: 1rem solid var(--highlight-bg);
|
||||
}
|
||||
|
||||
main {
|
||||
|
@ -135,10 +143,10 @@ main {
|
|||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
form .form-group button,
|
||||
form .form-group select,
|
||||
form .form-group textarea,
|
||||
form .form-group input {
|
||||
.form .form-group button,
|
||||
.form .form-group select,
|
||||
.form .form-group textarea,
|
||||
.form .form-group input {
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--default-bg);
|
||||
|
@ -147,14 +155,14 @@ form .form-group input {
|
|||
border: none;
|
||||
}
|
||||
|
||||
form .form-group button:hover,
|
||||
form .form-group select:hover,
|
||||
form .form-group textarea:hover,
|
||||
form .form-group input:hover,
|
||||
form .form-group button:focus,
|
||||
form .form-group select:focus,
|
||||
form .form-group textarea:focus,
|
||||
form .form-group input:focus {
|
||||
.form .form-group button:hover,
|
||||
.form .form-group select:hover,
|
||||
.form .form-group textarea:hover,
|
||||
.form .form-group input:hover,
|
||||
.form .form-group button:focus,
|
||||
.form .form-group select:focus,
|
||||
.form .form-group textarea:focus,
|
||||
.form .form-group input:focus {
|
||||
filter: brightness(1.5);
|
||||
}
|
||||
|
||||
|
@ -189,3 +197,15 @@ form .form-group button:hover {
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: rgba(255, 0, 0, 0.5);
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin: 1rem 0;
|
||||
min-width: 25vw;
|
||||
}
|
||||
|
||||
.warning h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
53
public/css/order.css
Normal file
53
public/css/order.css
Normal file
|
@ -0,0 +1,53 @@
|
|||
body {
|
||||
/* center with spacing on the left and right*/
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
padding: 0 20px 3rem;
|
||||
}
|
||||
|
||||
#seat-chooser {
|
||||
border: 1px solid black;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
#summary {
|
||||
background-color: var(--second-bg);
|
||||
border-radius: 5px;
|
||||
padding: 1rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
#summary-dyn .seat {
|
||||
background-color: var(--darkest-bg);
|
||||
color: var(--primary-color-text);
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
#summary-dyn .seat select.ticket-select {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--primary-color-text);
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
#summary-dyn .seat .seat-span {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--primary-color-text);
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
#summary-dyn .seat .price {
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--darkest-bg);
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
float: right;
|
||||
}
|
|
@ -12,8 +12,6 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
--seat-width: 2.5rem;
|
||||
--seat-height: var(--seat-width);
|
||||
|
@ -95,24 +93,7 @@
|
|||
filter: brightness(0.8);
|
||||
}
|
||||
|
||||
#load-screen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
div.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 2s linear infinite;
|
||||
#seat-chooser #seat-chooser-status {
|
||||
text-align: center;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
|
24
public/js/freq.js
Normal file
24
public/js/freq.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
class FreqManager {
|
||||
#store;
|
||||
static getInstance() {
|
||||
if (this._instance === null) {
|
||||
this._instance = new FreqManager();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.#store = {};
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.#store[key] = value;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.#store[key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.freq = FreqManager.getInstance();
|
106
public/js/order.js
Normal file
106
public/js/order.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
// DEPENDS ON seat-chooser.js
|
||||
(async () => {
|
||||
let scDiv = document.querySelector('#seat-chooser');
|
||||
let summaryDiv = document.querySelector('#summary-dyn');
|
||||
|
||||
let showingId = scDiv.getAttribute('data-showing-id');
|
||||
let ticketTypes = await (await fetch(`/api/showing/${showingId}/prices`)).json();
|
||||
|
||||
let sc = new SeatChooser(
|
||||
showingId,
|
||||
scDiv,
|
||||
(sc, s, add) => {
|
||||
// seat click callback
|
||||
// sc is the seat chooser
|
||||
// s is the seat // but it's better to look at sc.selectedSeats
|
||||
// add is a boolean indicating whether the seat is being added or removed
|
||||
|
||||
// update the summary
|
||||
summaryDiv.innerHTML = '';
|
||||
sc.selectedSeats.forEach((seat, index) => {
|
||||
// set seat ticket if ticket is not set
|
||||
if (!seat.ticket) seat.ticket = ticketTypes[0];
|
||||
let seatDiv = document.createElement('div');
|
||||
seatDiv.classList.add('seat');
|
||||
let ticketSelect = document.createElement('select');
|
||||
ticketTypes.forEach((ticketType, index) => {
|
||||
let option = document.createElement('option');
|
||||
option.value = index;
|
||||
option.innerText = ticketType.price_type;
|
||||
ticketSelect.appendChild(option);
|
||||
});
|
||||
ticketSelect.classList.add('ticket-select');
|
||||
ticketSelect.value = ticketTypes.indexOf(seat.ticket);
|
||||
ticketSelect.addEventListener('change', (e) => {
|
||||
seat.ticket = ticketTypes[e.target.value];
|
||||
updateTicket(sc, seat, seatDiv);
|
||||
updateTotal(sc);
|
||||
});
|
||||
seatDiv.appendChild(ticketSelect);
|
||||
|
||||
seatDiv.appendChild(document.createTextNode(' '));
|
||||
|
||||
let seatSpan = document.createElement('span');
|
||||
seatSpan.innerText = `Row ${seat.row}, Seat ${seat.col}`;
|
||||
seatSpan.classList.add('seat-span');
|
||||
seatDiv.appendChild(seatSpan);
|
||||
seatDiv.appendChild(document.createTextNode(' '));
|
||||
|
||||
let priceSpan = document.createElement('span');
|
||||
priceSpan.classList.add('price');
|
||||
seatDiv.appendChild(priceSpan);
|
||||
summaryDiv.appendChild(seatDiv);
|
||||
|
||||
updateTicket(sc, seat, seatDiv);
|
||||
});
|
||||
|
||||
updateTotal(sc);
|
||||
|
||||
if (sc.selectedSeats.length == 0) document.querySelector('button#order-button').disabled = true;
|
||||
else document.querySelector('button#order-button').disabled = false;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
window.freq.set('seat-chooser', sc);
|
||||
})();
|
||||
|
||||
let total = 0;
|
||||
|
||||
function updateTicket(sc, seat, seatDiv) {
|
||||
seatDiv.querySelector('span:last-child').innerText = `€${seat.ticket.price}`;
|
||||
}
|
||||
|
||||
function updateTotal(sc) {
|
||||
total = 0;
|
||||
sc.selectedSeats.forEach(_seat => {
|
||||
total += Number(_seat.ticket.price);
|
||||
});
|
||||
document.querySelector('#summary-total').innerText = `Total: €${total}`;
|
||||
}
|
||||
|
||||
document.querySelector('button#order-button').addEventListener('click', (e) => {
|
||||
let seats = [];
|
||||
window.sc.selectedSeats.forEach(seat => {
|
||||
seats.push({
|
||||
seat: seat.id,
|
||||
ticket: seat.ticket.id
|
||||
});
|
||||
});
|
||||
fetch(`/api/order`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
showing: sc.showingId,
|
||||
seats: seats
|
||||
})
|
||||
}).then(res => {
|
||||
if (res.ok) {
|
||||
window.location = '/order/complete';
|
||||
} else {
|
||||
alert('Error placing order');
|
||||
}
|
||||
});
|
||||
});
|
|
@ -26,19 +26,21 @@ class SeatChooser {
|
|||
status.style.textAlign = 'center';
|
||||
this.container.appendChild(status);
|
||||
}
|
||||
// set the message
|
||||
this.container.querySelector('#seat-chooser-status-message').innerText = msg;
|
||||
// set the message if it exists
|
||||
if (this.container.querySelector('#seat-chooser-status-message'))
|
||||
this.container.querySelector('#seat-chooser-status-message').innerText = msg;
|
||||
}
|
||||
|
||||
constructor(showingId, container, submitCallback) {
|
||||
constructor(showingId, container, seatClickCallback = (sc, s) => {}, submitCallback = null) {
|
||||
this.showingId = showingId; // id of the showing
|
||||
this.container = container; // the container to render the seat chooser in
|
||||
this.submitCallback = submitCallback; // callback function to call when the user submits the form
|
||||
|
||||
this.setLoadingStatus("Initializing...");
|
||||
|
||||
this.seats = []; // array of nulls and Seat objects
|
||||
this.submitCallback = submitCallback; // callback function to call when the user submits the form
|
||||
this.seatClickCallback = seatClickCallback; // callback function to call when the user clicks on a seat
|
||||
|
||||
this.seats = []; // array of nulls and Seat objects
|
||||
this.selectedSeats = []; // array of Seat objects
|
||||
|
||||
this.init();
|
||||
|
@ -171,15 +173,17 @@ class SeatChooser {
|
|||
});
|
||||
buttonContainer.appendChild(clearButton);
|
||||
|
||||
// submit button
|
||||
let submitButton = document.createElement('button');
|
||||
submitButton.classList.add('button');
|
||||
submitButton.style.margin = '1rem';
|
||||
submitButton.innerText = 'Submit';
|
||||
submitButton.addEventListener('click', () => {
|
||||
this.submitCallback(this);
|
||||
});
|
||||
buttonContainer.appendChild(submitButton);
|
||||
if (this.submitCallback !== null) {
|
||||
// submit button
|
||||
let submitButton = document.createElement('button');
|
||||
submitButton.classList.add('button');
|
||||
submitButton.style.margin = '1rem';
|
||||
submitButton.innerText = 'Submit';
|
||||
submitButton.addEventListener('click', () => {
|
||||
this.submitCallback(this);
|
||||
});
|
||||
buttonContainer.appendChild(submitButton);
|
||||
}
|
||||
this.container.appendChild(buttonContainer);
|
||||
}
|
||||
|
||||
|
@ -209,6 +213,7 @@ class SeatChooser {
|
|||
this.selectedSeats.push(seat);
|
||||
}
|
||||
}
|
||||
this.seatClickCallback(this, seat, this.selectedSeats.includes(seat));
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
|
@ -219,21 +224,3 @@ class SeatChooser {
|
|||
this.selectedSeats = [];
|
||||
}
|
||||
}
|
||||
|
||||
let seatChooser = new SeatChooser(1, document.getElementById('seat-chooser'), (sc) => {
|
||||
// gray overlay and show selected seats
|
||||
let overlay = document.createElement('div');
|
||||
overlay.style.position = 'fixed';
|
||||
overlay.style.top = '0';
|
||||
overlay.style.left = '0';
|
||||
overlay.style.width = '100vw';
|
||||
overlay.style.height = '100vh';
|
||||
overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
|
||||
overlay.style.display = 'flex';
|
||||
overlay.style.justifyContent = 'center';
|
||||
overlay.innerText = 'You selected the following seats: ' + sc.selectedSeats.map(seat => `${seat.row}-${seat.col}`).join(', ');
|
||||
overlay.addEventListener('click', () => {
|
||||
overlay.remove();
|
||||
});
|
||||
sc.container.appendChild(overlay);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue