tweaks for seat-chooser
This commit is contained in:
parent
b0cc5b5278
commit
2451ab45cb
4 changed files with 174 additions and 29 deletions
|
@ -47,15 +47,9 @@ public function seatMatrix($showing_id = null)
|
||||||
{
|
{
|
||||||
$seats = $this->seats;
|
$seats = $this->seats;
|
||||||
$matrix = [];
|
$matrix = [];
|
||||||
// first, create an empty matrix
|
|
||||||
for ($i = 0; $i < $this->room_rows-1; $i++) {
|
|
||||||
$matrix[$i] = [];
|
|
||||||
for ($j = 0; $j < $this->room_columns-1; $j++) {
|
|
||||||
$matrix[$i][$j] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// then, fill it with the seats
|
// then, fill it with the seats
|
||||||
foreach ($seats as $seat) {
|
foreach ($seats as $seat) {
|
||||||
|
if (!isset($matrix[$seat->seat_row - 1])) $matrix[$seat->seat_row - 1] = [];
|
||||||
$matrix[$seat->seat_row-1][$seat->seat_column-1] = [
|
$matrix[$seat->seat_row-1][$seat->seat_column-1] = [
|
||||||
'seat_id' => $seat->seat_id,
|
'seat_id' => $seat->seat_id,
|
||||||
'seat_row' => $seat->seat_row,
|
'seat_row' => $seat->seat_row,
|
||||||
|
@ -65,7 +59,24 @@ public function seatMatrix($showing_id = null)
|
||||||
'reserved' => $showing_id ? $seat->isReserved($showing_id) : false,
|
'reserved' => $showing_id ? $seat->isReserved($showing_id) : false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $matrix;
|
return fix_matrix($matrix, $this->room_columns-1, $this->room_rows-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fix_matrix($m, $h, $w) {
|
||||||
|
$_nm = [];
|
||||||
|
// write the matrix into _nm and fill the empty spaces with null
|
||||||
|
for ($i = 0; $i <= $h; $i++) {
|
||||||
|
$_nm[$i] = [];
|
||||||
|
for ($j = 0; $j <= $w; $j++) {
|
||||||
|
if (isset($m[$i][$j])) {
|
||||||
|
$_nm[$i][$j] = $m[$i][$j];
|
||||||
|
} else {
|
||||||
|
$_nm[$i][$j] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $_nm;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,12 @@ #seat-chooser {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
--seat-width: 2.5rem;
|
||||||
|
--seat-height: var(--seat-width);
|
||||||
|
--seat-gap: 0.5rem;
|
||||||
|
--seat-border-size: 0.1rem;
|
||||||
|
--seat-border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#seat-chooser .row {
|
#seat-chooser .row {
|
||||||
|
@ -29,12 +35,12 @@ #seat-chooser .row .seat {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 50px;
|
width: var(--seat-width);
|
||||||
height: 50px;
|
height: var(--seat-height);
|
||||||
margin: 5px;
|
margin: var(--seat-gap);
|
||||||
border: 1px solid #000;
|
border: var(--seat-border-size) solid var(--primary-color);
|
||||||
border-radius: 5px;
|
border-radius: var(--seat-border-radius);
|
||||||
background-color: #fff;
|
background-color: var(--second-bg);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,22 +56,39 @@ #seat-chooser .row .seat.linked-right {
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#seat-chooser .row .seat.loveseat {
|
||||||
|
background-color: #ff3fe5;
|
||||||
|
}
|
||||||
|
|
||||||
#seat-chooser .row .no-seat {
|
#seat-chooser .row .no-seat {
|
||||||
/* this seat DOES NOT EXISTS it's a filler for layout*/
|
/* this seat DOES NOT EXISTS it's a filler for layout*/
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
width: var(--seat-width);
|
||||||
margin: 5px;
|
height: var(--seat-height);
|
||||||
|
margin: calc(var(--seat-border-size) + var(--seat-gap));
|
||||||
}
|
}
|
||||||
|
|
||||||
#seat-chooser .row .seat.selected {
|
#seat-chooser .row .seat.selected {
|
||||||
background-color: #55f;
|
border: calc(var(--seat-gap) - 0.1rem) solid var(--primary-color);
|
||||||
|
margin: 0.1rem;
|
||||||
|
filter: brightness(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#seat-chooser .row .seat.reserved {
|
#seat-chooser .row .seat.reserved {
|
||||||
background-color: #f00;
|
background-color: var(--highlight-bg);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#seat-chooser .row .seat.disabled {
|
||||||
|
background-color: var(--default-bg);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#seat-chooser .row .seat.wheelchair {
|
||||||
|
background-color: #3498db;
|
||||||
}
|
}
|
||||||
|
|
||||||
#seat-chooser .row .seat:hover {
|
#seat-chooser .row .seat:hover {
|
||||||
|
|
|
@ -4,22 +4,38 @@
|
||||||
// the input is a matrix with null OR a seat object
|
// the input is a matrix with null OR a seat object
|
||||||
|
|
||||||
class Seat {
|
class Seat {
|
||||||
constructor(id, row, col, linked, reserved) {
|
constructor(id, row, col, linked, reserved, type) {
|
||||||
this.id = id; // database id
|
this.id = id; // database id
|
||||||
this.row = row;
|
this.row = row;
|
||||||
this.col = col;
|
this.col = col;
|
||||||
this.linked = linked; // linked seat
|
this.linked = linked; // linked seat
|
||||||
this.reserved = reserved;
|
this.reserved = reserved;
|
||||||
|
this.type = type; // seat type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SeatChooser {
|
class SeatChooser {
|
||||||
constructor(showingId, container) {
|
setLoadingStatus(msg) {
|
||||||
if (container.innerText === '') {
|
// if no status element exists, create one
|
||||||
container.innerText = 'Loading...';
|
if (!this.container.querySelector('#seat-chooser-status')) {
|
||||||
|
let status = document.createElement('div');
|
||||||
|
// h2 for "Loading..."
|
||||||
|
// span for message
|
||||||
|
status.innerHTML = `<h2>Loading...</h2><span id="seat-chooser-status-message"></span>`;
|
||||||
|
status.id = 'seat-chooser-status';
|
||||||
|
status.style.textAlign = 'center';
|
||||||
|
this.container.appendChild(status);
|
||||||
}
|
}
|
||||||
|
// set the message
|
||||||
|
this.container.querySelector('#seat-chooser-status-message').innerText = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(showingId, container, submitCallback) {
|
||||||
this.showingId = showingId; // id of the showing
|
this.showingId = showingId; // id of the showing
|
||||||
this.container = container; // the container to render the seat chooser in
|
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.seats = []; // array of nulls and Seat objects
|
||||||
|
|
||||||
|
@ -29,34 +45,55 @@ class SeatChooser {
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
this.setLoadingStatus("Fetching showing data...");
|
||||||
let showingJson = await (await fetch(`/api/showings/${this.showingId}`)).json();
|
let showingJson = await (await fetch(`/api/showings/${this.showingId}`)).json();
|
||||||
|
this.setLoadingStatus("Fetching seats...");
|
||||||
let roomMatrix = await (await fetch(`/api/showings/${this.showingId}/seatMatrix`)).json();
|
let roomMatrix = await (await fetch(`/api/showings/${this.showingId}/seatMatrix`)).json();
|
||||||
// Matrix<json | null> -> Matrix<Seat | null>
|
// Matrix<json | null> -> Matrix<Seat | null>
|
||||||
|
this.setLoadingStatus("Processing seats...");
|
||||||
console.log("Matrix<json | null> -> Matrix<Seat | null>");
|
console.log("Matrix<json | null> -> Matrix<Seat | null>");
|
||||||
this.seats = roomMatrix.map((row, rowIndex) => {
|
this.seats = roomMatrix.map((row, rowIndex) => {
|
||||||
return row.map((seat, colIndex) => {
|
return row.map((seat, colIndex) => {
|
||||||
if (seat === null) {
|
if (seat === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
this.setLoadingStatus("Processing seats... " + Math.round((rowIndex * row.length + colIndex) / (row.length * row.length) * 100) + "%");
|
||||||
return new Seat(
|
return new Seat(
|
||||||
seat['seat_id'],
|
seat['seat_id'],
|
||||||
seat['seat_row'],
|
seat['seat_row'],
|
||||||
seat['seat_column'],
|
seat['seat_column'],
|
||||||
seat['seat_linked_id'],
|
seat['seat_linked_id'],
|
||||||
seat['reserved']
|
seat['reserved'],
|
||||||
|
seat['seat_type']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
console.log(this.seats);
|
console.log(this.seats);
|
||||||
|
await this.fixCoronaSeating();
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fixCoronaSeating() {
|
||||||
|
// the seat left or right of a set of reserved seats are not available
|
||||||
|
this.seats.forEach((row, rowIndex) => {
|
||||||
|
row.forEach((seat, colIndex) => {
|
||||||
|
if (seat === null) return;
|
||||||
|
if (seat.reserved) {
|
||||||
|
if (row[colIndex - 1] && !row[colIndex - 1].reserved)
|
||||||
|
row[colIndex - 1].type = 'not_available';
|
||||||
|
if (row[colIndex + 1] && !row[colIndex + 1].reserved)
|
||||||
|
row[colIndex + 1].type = 'not_available';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log("rendering");
|
this.setLoadingStatus("Rendering...");
|
||||||
let linkedCache = false;
|
let linkedCache = false;
|
||||||
// render the seat chooser
|
// render the seat chooser
|
||||||
this.container.innerHTML = '';
|
|
||||||
// this -> row -> seat
|
// this -> row -> seat
|
||||||
|
this.container.innerHTML = '';
|
||||||
this.seats.forEach((row, rowIndex) => {
|
this.seats.forEach((row, rowIndex) => {
|
||||||
let rowDiv = document.createElement('div');
|
let rowDiv = document.createElement('div');
|
||||||
rowDiv.classList.add('row');
|
rowDiv.classList.add('row');
|
||||||
|
@ -64,7 +101,10 @@ class SeatChooser {
|
||||||
let seatDiv = document.createElement('div');
|
let seatDiv = document.createElement('div');
|
||||||
if (seat) {
|
if (seat) {
|
||||||
seatDiv.classList.add('seat');
|
seatDiv.classList.add('seat');
|
||||||
seatDiv.classList.add(seat.reserved ? 'reserved' : 'available');
|
if (seat.reserved) {
|
||||||
|
seatDiv.classList.add('reserved');
|
||||||
|
seatDiv.title = 'This seat is reserved';
|
||||||
|
}
|
||||||
|
|
||||||
if (seat.linked) {
|
if (seat.linked) {
|
||||||
// if cache is false, then this is the first seat in the linked pair
|
// if cache is false, then this is the first seat in the linked pair
|
||||||
|
@ -81,6 +121,27 @@ class SeatChooser {
|
||||||
|
|
||||||
seatDiv.innerText = `${seat.row}-${seat.col}`;
|
seatDiv.innerText = `${seat.row}-${seat.col}`;
|
||||||
|
|
||||||
|
switch (seat.type) {
|
||||||
|
case 'not_available':
|
||||||
|
seatDiv.title = 'This seat is not available';
|
||||||
|
seatDiv.classList.add('disabled');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'wheelchair':
|
||||||
|
seatDiv.title = 'This seat is wheelchair accessible';
|
||||||
|
seatDiv.classList.add('wheelchair');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'loveseat':
|
||||||
|
seatDiv.title = 'This seat is a loveseat';
|
||||||
|
seatDiv.classList.add('loveseat');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'standard':
|
||||||
|
seatDiv.title = 'This is a standard seat';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
seatDiv.dataset.id = seat.id;
|
seatDiv.dataset.id = seat.id;
|
||||||
seatDiv.dataset.row = seat.row;
|
seatDiv.dataset.row = seat.row;
|
||||||
seatDiv.dataset.col = seat.col;
|
seatDiv.dataset.col = seat.col;
|
||||||
|
@ -95,12 +156,37 @@ class SeatChooser {
|
||||||
});
|
});
|
||||||
this.container.appendChild(rowDiv);
|
this.container.appendChild(rowDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// button container
|
||||||
|
let buttonContainer = document.createElement('div');
|
||||||
|
buttonContainer.style.display = 'flex';
|
||||||
|
|
||||||
|
// clear button
|
||||||
|
let clearButton = document.createElement('button');
|
||||||
|
clearButton.classList.add('button');
|
||||||
|
clearButton.style.margin = '1rem';
|
||||||
|
clearButton.innerText = 'Clear';
|
||||||
|
clearButton.addEventListener('click', () => {
|
||||||
|
this.clearSelection();
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
this.container.appendChild(buttonContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSeat(seat, e) {
|
toggleSeat(seat, e) {
|
||||||
// toggle the seat
|
// toggle the seat
|
||||||
// because of linked seats, we need to toggle both seats
|
// because of linked seats, we need to toggle both seats
|
||||||
if (seat.reserved) return;
|
if (seat.reserved || seat.type == 'not_available') return;
|
||||||
e.target.classList.toggle('selected');
|
e.target.classList.toggle('selected');
|
||||||
if (seat.linked) {
|
if (seat.linked) {
|
||||||
// beware that you can click on the left or right seat
|
// beware that you can click on the left or right seat
|
||||||
|
@ -124,6 +210,30 @@ class SeatChooser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
this.selectedSeats.forEach(seat => {
|
||||||
|
let seatDiv = this.container.querySelector(`[data-id="${seat.id}"]`);
|
||||||
|
seatDiv.classList.remove('selected');
|
||||||
|
});
|
||||||
|
this.selectedSeats = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let seatChooser = new SeatChooser(1, document.getElementById('seat-chooser'));
|
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);
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@extends('layout')
|
@extends('layout')
|
||||||
|
|
||||||
@push('head')
|
@push('head')
|
||||||
|
<link rel="stylesheet" href="{{ asset('css/generic.css') }}">
|
||||||
<link rel="stylesheet" href="{{ asset('css/seat-chooser.css') }}">
|
<link rel="stylesheet" href="{{ asset('css/seat-chooser.css') }}">
|
||||||
<script src="{{ asset('js/seat-chooser.js') }}" defer></script>
|
<script src="{{ asset('js/seat-chooser.js') }}" defer></script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
Loading…
Reference in a new issue