From b0cc5b52787a37b35d146e885a2e977c56cf66c1 Mon Sep 17 00:00:00 2001 From: faulty Date: Sun, 1 Jan 2023 20:13:11 +0100 Subject: [PATCH] fix+add: seats + seat-chooser --- .../Controllers/Managing/CinemaController.php | 1 + .../Controllers/Managing/GenreController.php | 1 + .../Controllers/Managing/MovieController.php | 1 + app/Models/Address.php | 5 + app/Models/Room.php | 25 +++ app/Models/Seat.php | 20 +-- app/Models/Showing.php | 26 +++ app/Models/User.php | 3 - app/View/Components/SeatChooser.php | 18 ++- composer.json | 1 + composer.lock | 152 +++++++++++++++++- ...2022_11_22_000001_create_cinemas_table.php | 4 +- .../2022_11_22_000010_create_seats_table.php | 3 +- database/seeders/CinemaSeeder.php | 6 + docker-compose.yml | 31 ++-- public/css/generic.css | 4 + public/css/manage.css | 21 +++ public/css/seat-chooser.css | 110 +++++++++---- public/js/seat-chooser.js | 129 +++++++++++++++ .../components/seat-chooser.blade.old.php | 33 ++++ .../views/components/seat-chooser.blade.php | 23 +-- resources/views/main/cinemas/cinema.blade.php | 34 ++++ .../index.blade.php} | 0 resources/views/main/genres/genre.blade.php | 30 ++++ resources/views/main/movies/index.blade.php | 15 +- .../views/main/showings/showing.blade.php | 37 +++++ resources/views/manage/layout.blade.php | 5 +- .../views/manage/showings/index.blade.php | 32 ++++ .../views/manage/showings/showing.blade.php | 84 ++++++++++ routes/api.php | 25 +++ routes/web.php | 44 +++-- 31 files changed, 808 insertions(+), 115 deletions(-) create mode 100644 public/js/seat-chooser.js create mode 100644 resources/views/components/seat-chooser.blade.old.php create mode 100644 resources/views/main/cinemas/cinema.blade.php rename resources/views/main/{cinemas.blade.php => cinemas/index.blade.php} (100%) create mode 100644 resources/views/main/genres/genre.blade.php create mode 100644 resources/views/main/showings/showing.blade.php create mode 100644 resources/views/manage/showings/index.blade.php create mode 100644 resources/views/manage/showings/showing.blade.php diff --git a/app/Http/Controllers/Managing/CinemaController.php b/app/Http/Controllers/Managing/CinemaController.php index 07e78d0..4fcb3b2 100644 --- a/app/Http/Controllers/Managing/CinemaController.php +++ b/app/Http/Controllers/Managing/CinemaController.php @@ -8,6 +8,7 @@ public function __construct() { $this->middleware('auth'); $this->middleware('atleast:employee'); + $this->middleware('permission:manage_cinemas')->only(['create', 'store', 'edit', 'update', 'destroy']); } public function showAllCinemas() diff --git a/app/Http/Controllers/Managing/GenreController.php b/app/Http/Controllers/Managing/GenreController.php index 8aa5079..d1da3c0 100644 --- a/app/Http/Controllers/Managing/GenreController.php +++ b/app/Http/Controllers/Managing/GenreController.php @@ -10,6 +10,7 @@ class GenreController extends Controller public function __construct() { $this->middleware('auth'); $this->middleware('atleast:employee'); + $this->middleware('permission:manage_genres')->only(['create', 'store', 'edit', 'update', 'destroy']); } public function showAllGenres() { diff --git a/app/Http/Controllers/Managing/MovieController.php b/app/Http/Controllers/Managing/MovieController.php index 9c9ea8c..d4117ba 100644 --- a/app/Http/Controllers/Managing/MovieController.php +++ b/app/Http/Controllers/Managing/MovieController.php @@ -11,6 +11,7 @@ class MovieController extends Controller public function __construct() { $this->middleware('auth'); $this->middleware('atleast:employee'); + $this->middleware('permission:manage_movies')->only(['create', 'store', 'edit', 'update', 'destroy']); } public function showAllMovies() { diff --git a/app/Models/Address.php b/app/Models/Address.php index 3f10594..dc17fa6 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -29,5 +29,10 @@ public function user() return $this->belongsTo(User::class, 'user_id', 'id'); } + public function string() + { + return $this->address_street . ", " . $this->address_city . ", " . $this->address_state . " " . $this->address_zip . ", " . $this->address_country; + } + } diff --git a/app/Models/Room.php b/app/Models/Room.php index f9c5d79..207355f 100644 --- a/app/Models/Room.php +++ b/app/Models/Room.php @@ -43,4 +43,29 @@ public function cinema() return $this->belongsTo(Cinema::class, 'cinema_id', 'cinema_id'); } + 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) { + $matrix[$seat->seat_row-1][$seat->seat_column-1] = [ + 'seat_id' => $seat->seat_id, + 'seat_row' => $seat->seat_row, + 'seat_column' => $seat->seat_column, + 'seat_type' => $seat->seat_type, + 'seat_linked_id' => $seat->seat_linked_id, + 'reserved' => $showing_id ? $seat->isReserved($showing_id) : false, + ]; + } + return $matrix; + } + } diff --git a/app/Models/Seat.php b/app/Models/Seat.php index 09b2932..a288830 100644 --- a/app/Models/Seat.php +++ b/app/Models/Seat.php @@ -13,7 +13,8 @@ class Seat extends Model protected $fillable = [ 'seat_row', 'seat_number', - 'seat_type', // enum('standard', 'wheelchair', 'loveseat') + 'seat_type', // enum('standard', 'wheelchair', 'loveseat', 'not_available') + 'seat_linked_id', // if this is a loveseat, this is the other seat 'room_id', ]; @@ -27,28 +28,27 @@ public function room() return $this->belongsTo(Room::class, 'room_id', 'room_id'); } - public function orders() - { - return $this->belongsToMany(Order::class, 'order_seats', 'seat_id', 'order_id'); - } - public function tickets() { return $this->hasMany(Ticket::class, 'seat_id', 'seat_id'); } + public function linked_seat() + { + return $this->belongsTo(Seat::class, 'seat_linked_id', 'seat_id'); + } + // isReserved(int showing_id) method // Looks at showing / order / ticket if it's reserved // Returns true if it is reserved, false if it isn't - public function is_reserved(int $showing_id) + public function isReserved(int $showing_id): bool { - $tickets = $this->tickets()->where('showing_id', $showing_id)->get(); + $tickets = $this->tickets->where('showing_id', $showing_id); foreach ($tickets as $ticket) { - if ($ticket->order->order_status == 'pending' || $ticket->order->order_status == 'paid') { + if ($ticket->showing_id == $showing_id) { return true; } } return false; } - } diff --git a/app/Models/Showing.php b/app/Models/Showing.php index 45373c5..0c1d074 100644 --- a/app/Models/Showing.php +++ b/app/Models/Showing.php @@ -2,6 +2,7 @@ namespace App\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; /** @@ -45,4 +46,29 @@ public function nowPlaying() return $this->where('showing_start', '>=', now())->get(); } + public function end_time() { + $date = new Carbon($this->showing_start); + $date->addMinutes($this->movie->movie_length); + return $date; + } + + public function tickets() + { + return $this->hasMany(Ticket::class, 'showing_id', 'showing_id'); + } + + public function tickets_available() { + $tickets = $this->room->seats()->count(); + $tickets_sold = $this->tickets()->count(); + return $tickets - $tickets_sold; + } + + public function find($id) + { + return Showing::where('showing_id', $id)->first(); + } + + public function seatMatrix() { + return $this->room->seatMatrix($this->showing_id); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 39e1b6a..25df236 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -62,9 +62,6 @@ public function permissions() public function allowedTo($permission): bool { - if($this->role === 'manage') { - return true; - } if ($this->permissions()->where('permission_name', $permission)->first()) { return true; } diff --git a/app/View/Components/SeatChooser.php b/app/View/Components/SeatChooser.php index b3eaef9..7aead44 100644 --- a/app/View/Components/SeatChooser.php +++ b/app/View/Components/SeatChooser.php @@ -25,14 +25,20 @@ public function __construct(int $room_id, int $showing_id) } public function matrixGenerate() { - $matrix = []; - for ($row = 1; $row <= $this->room->room_rows; $row++) { - $matrix[$row] = []; - for ($column = 1; $column <= $this->room->room_columns; $column++) { - $matrix[$row][$column] = 0; + // returns a matrix of seats + $m = []; + // first make empty matrix + for ($i = 0; $i < $this->room->room_rows-1; $i++) { + $m[$i] = []; + for ($j = 0; $j < $this->room->room_columns-1; $j++) { + $m[$i][$j] = null; } } - return $matrix; + $seats = $this->room->seats; + foreach ($seats as $seat) { + $m[$seat->seat_row][$seat->seat_column] = $seat; + } + return $m; } /** diff --git a/composer.json b/composer.json index 8827713..2cb783b 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "laravel/tinker": "^2.7" }, "require-dev": { + "barryvdh/laravel-debugbar": "^3.7", "fakerphp/faker": "^1.9.1", "laravel/pint": "^1.0", "laravel/sail": "^1.0.1", diff --git a/composer.lock b/composer.lock index f09f7b8..4d22592 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8c432fe5b25b92e8fcd080943fcdb6a1", + "content-hash": "3ae872d179c399882f9ee7cadecede4c", "packages": [ { "name": "brick/math", @@ -5433,6 +5433,90 @@ } ], "packages-dev": [ + { + "name": "barryvdh/laravel-debugbar", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-debugbar.git", + "reference": "3372ed65e6d2039d663ed19aa699956f9d346271" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/3372ed65e6d2039d663ed19aa699956f9d346271", + "reference": "3372ed65e6d2039d663ed19aa699956f9d346271", + "shasum": "" + }, + "require": { + "illuminate/routing": "^7|^8|^9", + "illuminate/session": "^7|^8|^9", + "illuminate/support": "^7|^8|^9", + "maximebf/debugbar": "^1.17.2", + "php": ">=7.2.5", + "symfony/finder": "^5|^6" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench-dusk": "^5|^6|^7", + "phpunit/phpunit": "^8.5|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\Debugbar\\ServiceProvider" + ], + "aliases": { + "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Barryvdh\\Debugbar\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "PHP Debugbar integration for Laravel", + "keywords": [ + "debug", + "debugbar", + "laravel", + "profiler", + "webprofiler" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-debugbar/issues", + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2022-07-11T09:26:42+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.1", @@ -5879,6 +5963,72 @@ }, "time": "2022-11-15T14:36:57+00:00" }, + { + "name": "maximebf/debugbar", + "version": "v1.18.1", + "source": { + "type": "git", + "url": "https://github.com/maximebf/php-debugbar.git", + "reference": "ba0af68dd4316834701ecb30a00ce9604ced3ee9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/ba0af68dd4316834701ecb30a00ce9604ced3ee9", + "reference": "ba0af68dd4316834701ecb30a00ce9604ced3ee9", + "shasum": "" + }, + "require": { + "php": "^7.1|^8", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^2.6|^3|^4|^5|^6" + }, + "require-dev": { + "phpunit/phpunit": "^7.5.20 || ^9.4.2", + "twig/twig": "^1.38|^2.7|^3.0" + }, + "suggest": { + "kriswallsmith/assetic": "The best way to manage assets", + "monolog/monolog": "Log using Monolog", + "predis/predis": "Redis storage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + } + }, + "autoload": { + "psr-4": { + "DebugBar\\": "src/DebugBar/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Bouroumeau-Fuseau", + "email": "maxime.bouroumeau@gmail.com", + "homepage": "http://maximebf.com" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Debug bar in the browser for php application", + "homepage": "https://github.com/maximebf/php-debugbar", + "keywords": [ + "debug", + "debugbar" + ], + "support": { + "issues": "https://github.com/maximebf/php-debugbar/issues", + "source": "https://github.com/maximebf/php-debugbar/tree/v1.18.1" + }, + "time": "2022-03-31T14:55:54+00:00" + }, { "name": "mockery/mockery", "version": "1.5.1", diff --git a/database/migrations/2022_11_22_000001_create_cinemas_table.php b/database/migrations/2022_11_22_000001_create_cinemas_table.php index c224d89..476878b 100644 --- a/database/migrations/2022_11_22_000001_create_cinemas_table.php +++ b/database/migrations/2022_11_22_000001_create_cinemas_table.php @@ -19,8 +19,8 @@ public function up() $table->string('cinema_name'); $table->foreignId('address_id')->constrained('addresses', 'address_id'); $table->foreignId('user_id')->constrained('users', 'user_id'); // who created this cinema - $table->timestamp('cinema_open')->nullable(); - $table->timestamp('cinema_close')->nullable(); + $table->time('cinema_open'); + $table->time('cinema_close'); }); } diff --git a/database/migrations/2022_11_22_000010_create_seats_table.php b/database/migrations/2022_11_22_000010_create_seats_table.php index 723e42f..411a669 100644 --- a/database/migrations/2022_11_22_000010_create_seats_table.php +++ b/database/migrations/2022_11_22_000010_create_seats_table.php @@ -18,7 +18,8 @@ public function up() $table->timestamps(); $table->addColumn('integer', 'seat_row'); $table->addColumn('integer', 'seat_column'); - $table->addColumn('enum', 'seat_type', ['values' => ['standard', 'wheelchair', 'loveseat']]); + $table->enum('seat_type', ['standard', 'wheelchair', 'loveseat', 'not_available']); + $table->foreignId('seat_linked_id')->nullable()->constrained('seats', 'seat_id'); $table->foreignId('room_id')->constrained('rooms', 'room_id'); $table->unique(['room_id', 'seat_row', 'seat_column']); }); diff --git a/database/seeders/CinemaSeeder.php b/database/seeders/CinemaSeeder.php index 1bf5cb4..1db577d 100644 --- a/database/seeders/CinemaSeeder.php +++ b/database/seeders/CinemaSeeder.php @@ -25,6 +25,8 @@ public function run() 'address_zip' => '12345', 'address_phone' => '123-456-7890', ], + 'cinema_open' => '10:00:00', + 'cinema_close' => '22:00:00', ], [ 'cinema_name' => 'Cinema 2', @@ -36,6 +38,8 @@ public function run() 'address_zip' => '12345', 'address_phone' => '123-456-7890', ], + 'cinema_open' => '12:00:00', + 'cinema_close' => '23:00:00', ] ]; @@ -44,6 +48,8 @@ public function run() $c = new \App\Models\Cinema(); $c->cinema_name = $cinema['cinema_name']; $c->user_id = 1; + $c->cinema_open = $cinema['cinema_open']; + $c->cinema_close = $cinema['cinema_close']; $a = new \App\Models\Address(); $a->address_street = $cinema['cinema_address']['address_line_1']; diff --git a/docker-compose.yml b/docker-compose.yml index ca3b899..eb29913 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,25 +3,16 @@ version: '3.3' # LARAVEL ENVIRONMENT COMPOSE services: - redis: - image: redis:alpine - container_name: redis + phpmyadmin: + image: phpmyadmin/phpmyadmin + container_name: phpmyadmin restart: always ports: - - "6379:6379" - - memcached: - image: memcached:alpine - container_name: memcached - restart: always - ports: - - "11211:11211" - - mailhog: - image: mailhog/mailhog - container_name: mailhog - restart: always - ports: - - "1025:1025" - - "8025:8025" - + - "8080:80" + environment: + PMA_HOST: neo.faulty.nl + PMA_PORT: 33061 + PMA_USER: laravel + PMA_PASSWORD: laravel + PMA_ARBITRARY: 1 + PMA_ABSOLUTE_URI: http://localhost:8080/ diff --git a/public/css/generic.css b/public/css/generic.css index 8f94506..38e6229 100644 --- a/public/css/generic.css +++ b/public/css/generic.css @@ -171,3 +171,7 @@ .hide { .op0 { opacity: 0; } + +.mono { + font-family: monospace; +} diff --git a/public/css/manage.css b/public/css/manage.css index 2700105..bbac90a 100644 --- a/public/css/manage.css +++ b/public/css/manage.css @@ -168,3 +168,24 @@ form .form-group button:hover { filter: brightness(1.5); } +#showings { + display: flex; + flex-direction: column; +} + +#showings .showing { + display: grid; + grid-template-columns: 2fr 1fr 1fr 0.25fr; + gap: 0.5rem; + margin: 0.5rem 0; + padding: 1rem; + background: var(--second-bg); + border-radius: 0.5rem; +} + +#showings .showing .actions { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} diff --git a/public/css/seat-chooser.css b/public/css/seat-chooser.css index e72fc48..efa2923 100644 --- a/public/css/seat-chooser.css +++ b/public/css/seat-chooser.css @@ -1,39 +1,95 @@ -:root { - --spacing: 1rem; -} +/* + * (C) (C) (C) (C) (C) + (R) S S S S S + (R) S S S S S + (R) S S S S S + (R) S S S S S + ... + */ -.seat-chooser { - padding: var(--spacing); +#seat-chooser { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; width: 100%; height: 100%; - position: relative; - background: var(--second-bg); - border: 1px solid #ccc; - border-radius: 5px; - overflow: hidden; - - display: grid; - grid-template-columns: repeat(var(--cols), 1fr); - grid-template-rows: repeat(var(--rows), 1fr); - gap: var(--spacing); } -.seat-chooser .seat { - position: relative; - background: #5f5; - border: 1px solid #ccc; +#seat-chooser .row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +#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; - overflow: hidden; + background-color: #fff; cursor: pointer; - width: 32px; - height: 32px; - transition: all 0.3s ease; } -.seat-chooser .seat.seat-reserved { - background: #f66; +#seat-chooser .row .seat.linked-left { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; } -.seat-chooser .seat:hover { - background: var(--highlight-bg); +#seat-chooser .row .seat.linked-right { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 0; +} + +#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; +} + +#seat-chooser .row .seat.selected { + background-color: #55f; +} + +#seat-chooser .row .seat.reserved { + background-color: #f00; +} + +#seat-chooser .row .seat:hover { + 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; } diff --git a/public/js/seat-chooser.js b/public/js/seat-chooser.js new file mode 100644 index 0000000..44c5d3c --- /dev/null +++ b/public/js/seat-chooser.js @@ -0,0 +1,129 @@ +// this file should contain a class that gets info from /api/showings/{id} +// and renders the seat chooser +// there may be multiple seats selected at once +// the input is a matrix with null OR a seat object + +class Seat { + constructor(id, row, col, linked, reserved) { + this.id = id; // database id + this.row = row; + this.col = col; + this.linked = linked; // linked seat + this.reserved = reserved; + } +} + +class SeatChooser { + constructor(showingId, container) { + if (container.innerText === '') { + container.innerText = 'Loading...'; + } + this.showingId = showingId; // id of the showing + this.container = container; // the container to render the seat chooser in + + this.seats = []; // array of nulls and Seat objects + + this.selectedSeats = []; // array of Seat objects + + this.init(); + } + + async init() { + let showingJson = await (await fetch(`/api/showings/${this.showingId}`)).json(); + let roomMatrix = await (await fetch(`/api/showings/${this.showingId}/seatMatrix`)).json(); + // Matrix -> Matrix + console.log("Matrix -> Matrix"); + this.seats = roomMatrix.map((row, rowIndex) => { + return row.map((seat, colIndex) => { + if (seat === null) { + return null; + } + return new Seat( + seat['seat_id'], + seat['seat_row'], + seat['seat_column'], + seat['seat_linked_id'], + seat['reserved'] + ); + }); + }); + console.log(this.seats); + this.render(); + } + + render() { + console.log("rendering"); + let linkedCache = false; + // render the seat chooser + this.container.innerHTML = ''; + // this -> row -> seat + this.seats.forEach((row, rowIndex) => { + let rowDiv = document.createElement('div'); + rowDiv.classList.add('row'); + row.forEach((seat, colIndex) => { + let seatDiv = document.createElement('div'); + if (seat) { + seatDiv.classList.add('seat'); + seatDiv.classList.add(seat.reserved ? 'reserved' : 'available'); + + if (seat.linked) { + // if cache is false, then this is the first seat in the linked pair + if (!linkedCache) { + seatDiv.classList.add('linked-left'); + linkedCache = true; + } else { + seatDiv.classList.add('linked-right'); + linkedCache = false; + } + } else { + linkedCache = false; + } + + seatDiv.innerText = `${seat.row}-${seat.col}`; + + seatDiv.dataset.id = seat.id; + seatDiv.dataset.row = seat.row; + seatDiv.dataset.col = seat.col; + + seatDiv.addEventListener('click', (e) => { + this.toggleSeat(seat, e); + }); + } else { + seatDiv.classList.add('no-seat'); + } + rowDiv.appendChild(seatDiv); + }); + this.container.appendChild(rowDiv); + }); + } + + toggleSeat(seat, e) { + // toggle the seat + // because of linked seats, we need to toggle both seats + if (seat.reserved) return; + e.target.classList.toggle('selected'); + if (seat.linked) { + // beware that you can click on the left or right seat + // the left seat has class linked-left and the right seat has class linked-right + let leftbool = e.target.classList.contains('linked-left'); + let linkedSeat = this.seats[seat.row - 1][leftbool ? seat.col - 1 : seat.col + 1]; + if (leftbool) e.target.nextElementSibling.classList.toggle('selected'); + else e.target.previousElementSibling.classList.toggle('selected'); + if (this.selectedSeats.includes(linkedSeat)) { + this.selectedSeats.splice(this.selectedSeats.indexOf(linkedSeat), 1); + this.selectedSeats.splice(this.selectedSeats.indexOf(seat), 1); + } else { + this.selectedSeats.push(linkedSeat); + this.selectedSeats.push(seat); + } + } else { + if (this.selectedSeats.includes(seat)) { + this.selectedSeats.splice(this.selectedSeats.indexOf(seat), 1); + } else { + this.selectedSeats.push(seat); + } + } + } +} + +let seatChooser = new SeatChooser(1, document.getElementById('seat-chooser')); diff --git a/resources/views/components/seat-chooser.blade.old.php b/resources/views/components/seat-chooser.blade.old.php new file mode 100644 index 0000000..faa6102 --- /dev/null +++ b/resources/views/components/seat-chooser.blade.old.php @@ -0,0 +1,33 @@ +@extends('layout') + +@push('head') + + +@endpush + +
+
+
+
Loading...
+
+
+ +
+ @foreach($seatmatrix as $row) +
+ @foreach($row as $seat) + @if($seat) +
+
{{ $seat->seat_row }}-{{ $seat->seat_column }}
+
+ @else +
+ @endif + @endforeach +
+ @endforeach +
diff --git a/resources/views/components/seat-chooser.blade.php b/resources/views/components/seat-chooser.blade.php index cf192de..4133fe4 100644 --- a/resources/views/components/seat-chooser.blade.php +++ b/resources/views/components/seat-chooser.blade.php @@ -2,26 +2,7 @@ @push('head') - + @endpush -
- @foreach($seatmatrix as $row) -
- @foreach($row as $seat) -
-
{{ $seat->seat_row }}{{ $seat->seat_column }}
-
- @endforeach -
- @endforeach -
+
diff --git a/resources/views/main/cinemas/cinema.blade.php b/resources/views/main/cinemas/cinema.blade.php new file mode 100644 index 0000000..eb94aa5 --- /dev/null +++ b/resources/views/main/cinemas/cinema.blade.php @@ -0,0 +1,34 @@ +@extends('main.layout') + +@section('content') + +
+

{{$cinema->cinema_name}}

+
+
+
+ {{$cinema->address->string()}}
+ {{$cinema->cinema_open}} - {{$cinema->cinema_close}} +
+
+
+ +
+

Now playing:

+
+ +
+ +@endsection diff --git a/resources/views/main/cinemas.blade.php b/resources/views/main/cinemas/index.blade.php similarity index 100% rename from resources/views/main/cinemas.blade.php rename to resources/views/main/cinemas/index.blade.php diff --git a/resources/views/main/genres/genre.blade.php b/resources/views/main/genres/genre.blade.php new file mode 100644 index 0000000..ad0af9c --- /dev/null +++ b/resources/views/main/genres/genre.blade.php @@ -0,0 +1,30 @@ +@extends('main.layout') + +@section('content') + +
+

{{$genre->genre_name}}

+
+
+
+ {{$genre->genre_description}}
+
+
+
+ +
+

Movies:

+
+
+ @foreach($genre->movies as $movie) + + {{$movie->movie_name}} Poster +
+ {{$movie->movie_name}}
+ {{$movie->movie_length}} min +
+
+ @endforeach +
+
+@endsection diff --git a/resources/views/main/movies/index.blade.php b/resources/views/main/movies/index.blade.php index 4011617..c11e4c8 100644 --- a/resources/views/main/movies/index.blade.php +++ b/resources/views/main/movies/index.blade.php @@ -1,20 +1,17 @@ @extends('main.layout') +@push('head') + +@endpush + @section('content')

Now playing:


- @foreach($showings as $showing) - - {{$showing->movie->movie_name}} Poster -
- {{$showing->movie->movie_name}}
- {{$showing->movie->movie_length}} min -
-
+ @foreach($movies as $showing) + @endforeach
diff --git a/resources/views/main/showings/showing.blade.php b/resources/views/main/showings/showing.blade.php new file mode 100644 index 0000000..052eebf --- /dev/null +++ b/resources/views/main/showings/showing.blade.php @@ -0,0 +1,37 @@ +@extends('main.layout') + +@section('content') + +
+

{{$showing->movie->movie_name}} Poster + {{$showing->movie->movie_name}}

+
+
+
+ Cinema: {{$showing->room->cinema->cinema_name}}
+ Showing: {{$showing->showing_start}} - {{$showing->end_time()}}
+
+
+
+ +
+

Details

+
+ {{$showing->movie->movie_description}}
+
+ Runtime: {{$showing->movie->movie_length }} minutes
+ Year: {{$showing->movie->movie_year}}
+ Age limit: {{$showing->movie->movie_age_limit}}
+ Genre: {{$showing->movie->genre->genre_name}}
+
+ +
+

Tickets:

+
+ Tickets available: {{$showing->tickets_available()}}
+ Buy tickets? +{{-- todo--}} +
+@endsection diff --git a/resources/views/manage/layout.blade.php b/resources/views/manage/layout.blade.php index 9ce0f94..d3e5b22 100644 --- a/resources/views/manage/layout.blade.php +++ b/resources/views/manage/layout.blade.php @@ -1,7 +1,7 @@ {{-- Layout for admins --}} {{-- Will have a sidebar with links --}} {{-- Will have a top bar with account and link to main site--}} -{{--@extends('layout')--}} +@extends('layout') @push('head') @@ -19,9 +19,10 @@
diff --git a/resources/views/manage/showings/index.blade.php b/resources/views/manage/showings/index.blade.php new file mode 100644 index 0000000..3a3d164 --- /dev/null +++ b/resources/views/manage/showings/index.blade.php @@ -0,0 +1,32 @@ +@extends('manage.layout') + +@section('content') +

Showing Management

+
+ @foreach($showings as $showing) +
+
+

{{ $showing->movie->movie_name }}

+

{{ $showing->movie->movie_description }}

+

{{ $showing->movie->genre->genre_name }}

+
+
+

{{ $showing->room->cinema->cinema_name }} - {{ $showing->room->room_name }}

+

{{ $showing->room->cinema->address->string() }}

+
+
+

Times

+ + Start: {{ $showing->showing_start }}
+ Ends : {{ $showing->end_time() }} +
+
+
+ Edit +
+ +
+ @endforeach +
+ Create Showing +@endsection diff --git a/resources/views/manage/showings/showing.blade.php b/resources/views/manage/showings/showing.blade.php new file mode 100644 index 0000000..bb0d852 --- /dev/null +++ b/resources/views/manage/showings/showing.blade.php @@ -0,0 +1,84 @@ +@extends('manage.layout') + +@section('content') +

Showing - {{ $showing->showing_start }}

+
+ @csrf + @method('PUT') +
+ + +
+
+ + +
+
+ + +
+
+ + + +
+ +
+ +
+ +
+
+ @csrf + @method('DELETE') + +
+@endsection diff --git a/routes/api.php b/routes/api.php index eb6fa48..1e6977d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -17,3 +17,28 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); + +// /api/cinemas/{id}/rooms +Route::get('/cinemas/{id}/rooms', function ($id) { + $cinema = (new App\Models\Cinema)->find($id); + return $cinema->rooms; +}); + +// /api/showings/{id} +Route::get('/showings/{id}', function ($id) { + $showing = (new App\Models\Showing)->find($id); + return $showing; +}); + + +// /api/rooms/{id}/seatMatrix +Route::get('/rooms/{id}/seatMatrix', function ($id) { + $room = (new App\Models\Room)->find($id); + return $room->seatMatrix(); +}); + +// /api/showings/{id}/seatMatrix +Route::get('/showings/{id}/seatMatrix', function ($id) { + $showing = (new App\Models\Showing)->find($id); + return $showing->seatMatrix(); +}); diff --git a/routes/web.php b/routes/web.php index 3afbee2..8dc5b90 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,25 +21,15 @@ Route::get('/movies', function () { $s = new \App\Models\Showing(); - return view('main.movies', ['title' => "Movies", "showings" => $s->nowPlaying()->unique('movie_id')]); + return view('main.movies.index', ['title' => "Movies", "showings" => $s->nowPlaying()->unique('movie_id')]); })->name('movies'); Route::get('/movie/{id}', function ($id) { $m = new \App\Models\Movie(); $movie = $m->find($id); - return view('main.movie', ['title' => $movie->movie_name, "movie" => $movie]); + return view('main.movies.movie', ['title' => $movie->movie_name, "movie" => $movie]); })->name('movie'); -Route::get('/cinemas', function () { - return view('main.cinemas', ['title' => "Cinemas", "cinemas" => \App\Models\Cinema::all()]); -})->name('cinemas'); - -Route::get('/cinema/{id}', function ($id) { - $c = new \App\Models\Cinema(); - $cinema = $c->find($id); - return view('main.cinema', ['title' => $cinema->cinema_name, "cinema" => $cinema]); -})->name('cinema'); - Auth::routes(); // account @@ -50,6 +40,19 @@ // CRUD - Create Read Update Delete // FB - Frontend Backend +// main +Route::get('/cinemas', [App\Http\Controllers\Main\CinemaController::class, 'showAllCinemas'])->name('cinemas'); +Route::get('/cinema/{id}', [App\Http\Controllers\Main\CinemaController::class, 'show'])->name('cinema'); + +Route::get('/showings', [App\Http\Controllers\Main\ShowingController::class, 'showAllShowings'])->name('showings'); +Route::get('/showing/{id}', [App\Http\Controllers\Main\ShowingController::class, 'show'])->name('showing'); + +Route::get('/movies', [App\Http\Controllers\Main\MovieController::class, 'showAllMovies'])->name('movies'); +Route::get('/movie/{id}', [App\Http\Controllers\Main\MovieController::class, 'show'])->name('movie'); + +Route::get('/genres', [App\Http\Controllers\Main\GenreController::class, 'showAllGenres'])->name('genres'); +Route::get('/genre/{id}', [App\Http\Controllers\Main\GenreController::class, 'show'])->name('genre'); + // Employee Home Page Route::get('/manage', function () { if (!auth()->user()->atleast('employee')) { @@ -114,11 +117,26 @@ Route::delete('/manage/cinema/{id}', [\App\Http\Controllers\Managing\CinemaController::class, 'destroy'])->name('manage.cinema'); }); +Route::controller(\App\Http\Controllers\Managing\ShowingsController::class)->group(function () { + // /manage/showings - CR showings (FB) + Route::get('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'showAllShowings'])->name('manage.showings'); + Route::post('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'store'])->name('manage.showings'); + + // /manage/showings/create - C showing (F) + Route::get('/manage/showings/create', [\App\Http\Controllers\Managing\ShowingsController::class, 'createShowing'])->name('manage.showings.create'); + // no post, handled by POST /showings + + // /manage/showings/{id} - RUD showing (FB) + Route::get('/manage/showing/{id}', [\App\Http\Controllers\Managing\ShowingsController::class, 'edit'])->name('manage.showing'); + Route::put('/manage/showing/{id}', [\App\Http\Controllers\Managing\ShowingsController::class, 'update'])->name('manage.showing'); + Route::delete('/manage/showing/{id}', [\App\Http\Controllers\Managing\ShowingsController::class, 'destroy'])->name('manage.showing'); +}); + // /test/comp/{component} Route::get('/test/comp/{component}', function ($component) { switch ($component) { case 'seat-chooser': - $c = new \App\View\Components\SeatChooser(1); + $c = new \App\View\Components\SeatChooser(1, 1); return $c->render(); default: return "No component found";