diff --git a/app/Models/Room.php b/app/Models/Room.php
index 207355f..f5e628b 100644
--- a/app/Models/Room.php
+++ b/app/Models/Room.php
@@ -47,15 +47,9 @@ public function seatMatrix($showing_id = null)
{
$seats = $this->seats;
$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
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] = [
'seat_id' => $seat->seat_id,
'seat_row' => $seat->seat_row,
@@ -65,7 +59,24 @@ public function seatMatrix($showing_id = null)
'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;
+}
diff --git a/public/css/seat-chooser.css b/public/css/seat-chooser.css
index efa2923..860d90b 100644
--- a/public/css/seat-chooser.css
+++ b/public/css/seat-chooser.css
@@ -14,6 +14,12 @@ #seat-chooser {
justify-content: center;
width: 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 {
@@ -29,12 +35,12 @@ #seat-chooser .row .seat {
display: flex;
align-items: center;
justify-content: center;
- width: 50px;
- height: 50px;
- margin: 5px;
- border: 1px solid #000;
- border-radius: 5px;
- background-color: #fff;
+ width: var(--seat-width);
+ height: var(--seat-height);
+ margin: var(--seat-gap);
+ border: var(--seat-border-size) solid var(--primary-color);
+ border-radius: var(--seat-border-radius);
+ background-color: var(--second-bg);
cursor: pointer;
}
@@ -50,22 +56,39 @@ #seat-chooser .row .seat.linked-right {
border-left: 0;
}
+#seat-chooser .row .seat.loveseat {
+ background-color: #ff3fe5;
+}
+
#seat-chooser .row .no-seat {
/* this seat DOES NOT EXISTS it's a filler for layout*/
border: none;
background-color: transparent;
cursor: default;
- width: 50px;
- height: 50px;
- margin: 5px;
+
+ width: var(--seat-width);
+ height: var(--seat-height);
+ margin: calc(var(--seat-border-size) + var(--seat-gap));
}
#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 {
- 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 {
diff --git a/public/js/seat-chooser.js b/public/js/seat-chooser.js
index 44c5d3c..4bae2c5 100644
--- a/public/js/seat-chooser.js
+++ b/public/js/seat-chooser.js
@@ -4,22 +4,38 @@
// the input is a matrix with null OR a seat object
class Seat {
- constructor(id, row, col, linked, reserved) {
+ constructor(id, row, col, linked, reserved, type) {
this.id = id; // database id
this.row = row;
this.col = col;
this.linked = linked; // linked seat
this.reserved = reserved;
+ this.type = type; // seat type
}
}
class SeatChooser {
- constructor(showingId, container) {
- if (container.innerText === '') {
- container.innerText = 'Loading...';
+ setLoadingStatus(msg) {
+ // if no status element exists, create one
+ if (!this.container.querySelector('#seat-chooser-status')) {
+ let status = document.createElement('div');
+ // h2 for "Loading..."
+ // span for message
+ status.innerHTML = `
Loading...
`;
+ 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.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
@@ -29,34 +45,55 @@ class SeatChooser {
}
async init() {
+ this.setLoadingStatus("Fetching showing data...");
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();
// Matrix -> Matrix
+ this.setLoadingStatus("Processing seats...");
console.log("Matrix -> Matrix");
this.seats = roomMatrix.map((row, rowIndex) => {
return row.map((seat, colIndex) => {
if (seat === null) {
return null;
}
+ this.setLoadingStatus("Processing seats... " + Math.round((rowIndex * row.length + colIndex) / (row.length * row.length) * 100) + "%");
return new Seat(
seat['seat_id'],
seat['seat_row'],
seat['seat_column'],
seat['seat_linked_id'],
- seat['reserved']
+ seat['reserved'],
+ seat['seat_type']
);
});
});
console.log(this.seats);
+ await this.fixCoronaSeating();
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() {
- console.log("rendering");
+ this.setLoadingStatus("Rendering...");
let linkedCache = false;
// render the seat chooser
- this.container.innerHTML = '';
// this -> row -> seat
+ this.container.innerHTML = '';
this.seats.forEach((row, rowIndex) => {
let rowDiv = document.createElement('div');
rowDiv.classList.add('row');
@@ -64,7 +101,10 @@ class SeatChooser {
let seatDiv = document.createElement('div');
if (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 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}`;
+ 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.row = seat.row;
seatDiv.dataset.col = seat.col;
@@ -95,12 +156,37 @@ class SeatChooser {
});
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) {
// toggle the seat
// 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');
if (seat.linked) {
// 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);
+});
diff --git a/resources/views/components/seat-chooser.blade.php b/resources/views/components/seat-chooser.blade.php
index 4133fe4..da51a17 100644
--- a/resources/views/components/seat-chooser.blade.php
+++ b/resources/views/components/seat-chooser.blade.php
@@ -1,6 +1,7 @@
@extends('layout')
@push('head')
+
@endpush