megacommit
This commit is contained in:
parent
2451ab45cb
commit
34ed81516b
51 changed files with 1200 additions and 251 deletions
12
.github/workflows/pull-requests.yml
vendored
12
.github/workflows/pull-requests.yml
vendored
|
@ -1,12 +0,0 @@
|
||||||
name: pull requests
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
uneditable:
|
|
||||||
uses: laravel/.github/.github/workflows/pull-requests.yml@main
|
|
47
.github/workflows/tests.yml
vendored
47
.github/workflows/tests.yml
vendored
|
@ -1,47 +0,0 @@
|
||||||
name: Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- '*.x'
|
|
||||||
pull_request:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tests:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
php: ['8.0', 8.1, 8.2]
|
|
||||||
|
|
||||||
name: PHP ${{ matrix.php }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
uses: shivammathur/setup-php@v2
|
|
||||||
with:
|
|
||||||
php-version: ${{ matrix.php }}
|
|
||||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
|
|
||||||
coverage: none
|
|
||||||
|
|
||||||
- name: Install Composer dependencies
|
|
||||||
run: composer install --prefer-dist --no-interaction --no-progress
|
|
||||||
|
|
||||||
- name: Copy environment file
|
|
||||||
run: cp .env.example .env
|
|
||||||
|
|
||||||
- name: Generate app key
|
|
||||||
run: php artisan key:generate
|
|
||||||
|
|
||||||
- name: Execute tests
|
|
||||||
run: vendor/bin/phpunit
|
|
13
.github/workflows/update-changelog.yml
vendored
13
.github/workflows/update-changelog.yml
vendored
|
@ -1,13 +0,0 @@
|
||||||
name: update changelog
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update:
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
uses: laravel/.github/.github/workflows/update-changelog.yml@main
|
|
19
app/Http/Controllers/Main/CinemaController.php
Normal file
19
app/Http/Controllers/Main/CinemaController.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Main;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CinemaController extends Controller
|
||||||
|
{
|
||||||
|
public function showAllCinemas()
|
||||||
|
{
|
||||||
|
return view('main.cinemas.index', ['title' => "Cinemas", 'cinemas' => \App\Models\Cinema::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('main.cinemas.cinema', ['title' => "Cinema", 'cinema' => \App\Models\Cinema::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
}
|
19
app/Http/Controllers/Main/GenreController.php
Normal file
19
app/Http/Controllers/Main/GenreController.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Main;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class GenreController extends Controller
|
||||||
|
{
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('main.genres.genre', ['title' => "Genre", 'genre' => \App\Models\Genre::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showAllGenres()
|
||||||
|
{
|
||||||
|
return view('main.genres.index', ['title' => "Genres", 'genres' => \App\Models\Genre::all()]);
|
||||||
|
}
|
||||||
|
}
|
35
app/Http/Controllers/Main/MovieController.php
Normal file
35
app/Http/Controllers/Main/MovieController.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Main;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class MovieController extends Controller
|
||||||
|
{
|
||||||
|
public function showAllMovies()
|
||||||
|
{
|
||||||
|
return view('main.movies.index', ['title' => "Movies", 'movies' => \App\Models\Movie::all(), 'genres' => \App\Models\Genre::all(), 'showings' => \App\Models\Showing::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function moviesNowShowing()
|
||||||
|
{
|
||||||
|
// map showings that are in the future to movies
|
||||||
|
$showings = \App\Models\Showing::all()->filter(function ($showing) {
|
||||||
|
return $showing->showing_start > now();
|
||||||
|
});
|
||||||
|
// $movies must be a collection of unique movies
|
||||||
|
$movies = collect();
|
||||||
|
foreach ($showings as $showing) {
|
||||||
|
if (!$movies->contains($showing->movie)) {
|
||||||
|
$movies->push($showing->movie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view('main.movies.index', ['title' => "Movies Now Showing", 'movies' => $movies, 'genres' => \App\Models\Genre::all(), 'showings' => \App\Models\Showing::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('main.movies.movie', ['title' => "Movie", 'movie' => \App\Models\Movie::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
}
|
24
app/Http/Controllers/Main/ShowingController.php
Normal file
24
app/Http/Controllers/Main/ShowingController.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Main;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ShowingController extends Controller
|
||||||
|
{
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('main.showings.showing', ['title' => "Showing", 'showing' => \App\Models\Showing::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showAllShowings()
|
||||||
|
{
|
||||||
|
return view('main.showings.index', ['title' => "Showings", 'showings' => \App\Models\Showing::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function order($id)
|
||||||
|
{
|
||||||
|
return view('main.order', ['title' => "Order Tickets", 'showing' => \App\Models\Showing::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,15 @@ public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
$this->middleware('atleast:employee');
|
$this->middleware('atleast:employee');
|
||||||
$this->middleware('permission:manage_cinemas')->only(['create', 'store', 'edit', 'update', 'destroy']);
|
$this->middleware('permission:READ_CINEMAS')->only('index', 'show');
|
||||||
|
$this->middleware('permission:CREATE_CINEMAS')->only('create', 'store');
|
||||||
|
$this->middleware('permission:UPDATE_CINEMAS')->only('edit', 'update');
|
||||||
|
$this->middleware('permission:DELETE_CINEMAS')->only('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showAllCinemas()
|
public function index()
|
||||||
{
|
{
|
||||||
return view('manage.cinemas.index', ['title' => "Manage Cinemas", 'cinemas' => \App\Models\Cinema::all()]);
|
return view('manage.cinemas.index', ['title' => "Manage Cinemas", 'cinemas' => \App\Models\Cinema::all(), 'users' => \App\Models\User::all()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
|
|
|
@ -10,10 +10,13 @@ class GenreController extends Controller
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
$this->middleware('atleast:employee');
|
$this->middleware('atleast:employee');
|
||||||
$this->middleware('permission:manage_genres')->only(['create', 'store', 'edit', 'update', 'destroy']);
|
$this->middleware('permission:READ_GENRES')->only('index', 'show');
|
||||||
|
$this->middleware('permission:CREATE_GENRES')->only('create', 'store');
|
||||||
|
$this->middleware('permission:UPDATE_GENRES')->only('edit', 'update');
|
||||||
|
$this->middleware('permission:DELETE_GENRES')->only('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showAllGenres() {
|
public function index() {
|
||||||
return view('manage.genres.index', ['title' => "Manage Genres", 'genres' => \App\Models\Genre::all()]);
|
return view('manage.genres.index', ['title' => "Manage Genres", 'genres' => \App\Models\Genre::all()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,13 @@ class MovieController extends Controller
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
$this->middleware('atleast:employee');
|
$this->middleware('atleast:employee');
|
||||||
$this->middleware('permission:manage_movies')->only(['create', 'store', 'edit', 'update', 'destroy']);
|
$this->middleware('permission:READ_MOVIES')->only('index', 'show');
|
||||||
|
$this->middleware('permission:CREATE_MOVIES')->only('create', 'store');
|
||||||
|
$this->middleware('permission:UPDATE_MOVIES')->only('edit', 'update');
|
||||||
|
$this->middleware('permission:DELETE_MOVIES')->only('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showAllMovies() {
|
public function index() {
|
||||||
// has permission READ_MOVIES ?
|
|
||||||
if(!Auth::user()->allowedTo('READ_MOVIES')) {
|
|
||||||
abort(403);
|
|
||||||
}
|
|
||||||
return view('manage.movies.index', ['title' => "Manage Movies", 'movies' => \App\Models\Movie::all()]);
|
return view('manage.movies.index', ['title' => "Manage Movies", 'movies' => \App\Models\Movie::all()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +36,8 @@ public function store() {
|
||||||
$movie->movie_description = request('movie_description');
|
$movie->movie_description = request('movie_description');
|
||||||
$movie->movie_year = request('movie_year');
|
$movie->movie_year = request('movie_year');
|
||||||
$movie->movie_image = request('movie_image');
|
$movie->movie_image = request('movie_image');
|
||||||
|
$movie->movie_length = request('movie_length');
|
||||||
|
$movie->movie_age_limit = request('movie_age_limit');
|
||||||
// $movie->user_id = auth()->user()->user_id;
|
// $movie->user_id = auth()->user()->user_id;
|
||||||
$movie->genre_id = request('genre_id');
|
$movie->genre_id = request('genre_id');
|
||||||
$movie->save();
|
$movie->save();
|
||||||
|
|
66
app/Http/Controllers/Managing/ShowingsController.php
Normal file
66
app/Http/Controllers/Managing/ShowingsController.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Managing;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
class ShowingsController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
$this->middleware('atleast:employee');
|
||||||
|
$this->middleware('permission:READ_SHOWINGS')->only('index', 'show');
|
||||||
|
$this->middleware('permission:CREATE_SHOWINGS')->only('create', 'store');
|
||||||
|
$this->middleware('permission:UPDATE_SHOWINGS')->only('edit', 'update');
|
||||||
|
$this->middleware('permission:DELETE_SHOWINGS')->only('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('manage.showings.index', ['title' => "Manage Showings", 'showings' => \App\Models\Showing::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit($id)
|
||||||
|
{
|
||||||
|
$s = \App\Models\Showing::findOrfail($id);
|
||||||
|
return view('manage.showings.showing', ['title' => "Manage Showing", 'showing' => $s, 'movies' => \App\Models\Movie::all(), 'rooms' => \App\Models\Room::all(), 'cinemas' => \App\Models\Cinema::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createShowing()
|
||||||
|
{
|
||||||
|
return view('manage.showings.create', ['title' => "Create Showing", 'movies' => \App\Models\Movie::all(), 'rooms' => \App\Models\Room::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
$showing = new \App\Models\Showing();
|
||||||
|
$showing->movie_id = request('movie_id');
|
||||||
|
$showing->room_id = request('room_id');
|
||||||
|
$showing->start_time = request('start_time');
|
||||||
|
$showing->save();
|
||||||
|
return redirect()->route('manage.showings');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('main.showings.showing', ['title' => "Edit Showing", 'showing' => \App\Models\Showing::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($id)
|
||||||
|
{
|
||||||
|
$showing = \App\Models\Showing::findOrfail($id);
|
||||||
|
$showing->movie_id = request('movie_id');
|
||||||
|
$showing->room_id = request('room_id');
|
||||||
|
$showing->showing_start = request('showing_start');
|
||||||
|
$showing->save();
|
||||||
|
return redirect()->route('manage.showings');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
$showing = \App\Models\Showing::findOrfail($id);
|
||||||
|
$showing->delete();
|
||||||
|
return redirect()->route('manage.showings');
|
||||||
|
}
|
||||||
|
}
|
81
app/Http/Controllers/Managing/UsersController.php
Normal file
81
app/Http/Controllers/Managing/UsersController.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Managing;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
class UsersController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
$this->middleware('atleast:manage');
|
||||||
|
$this->middleware('permission:READ_USERS')->only('index', 'show');
|
||||||
|
$this->middleware('permission:CREATE_USERS')->only('create', 'store');
|
||||||
|
$this->middleware('permission:UPDATE_USERS')->only('edit', 'update');
|
||||||
|
$this->middleware('permission:DELETE_USERS')->only('destroy');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('manage.users.index', ['title' => "Manage Users", 'users' => \App\Models\User::all()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return view('manage.users.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
$user = new \App\Models\User();
|
||||||
|
$user->name = request('name');
|
||||||
|
$user->email = request('email');
|
||||||
|
$user->password = request('password');
|
||||||
|
$user->role = request('role');
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
// user assignments (user_assignments.user_id, user_assignments.cinema_id)
|
||||||
|
$cinemas = request('cinemas');
|
||||||
|
if ($cinemas) {
|
||||||
|
foreach ($cinemas as $cinema) {
|
||||||
|
$user->cinemas()->attach($cinema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('manage.users');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return view('manage.users.user', ['user' => \App\Models\User::findOrfail($id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($id)
|
||||||
|
{
|
||||||
|
$user = \App\Models\User::findOrfail($id);
|
||||||
|
$user->name = request('name');
|
||||||
|
$user->email = request('email');
|
||||||
|
$user->password = request('password');
|
||||||
|
$user->role = request('role');
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
// user assignments (user_assignments.user_id, user_assignments.cinema_id)
|
||||||
|
$cinemas = request('cinemas');
|
||||||
|
if ($cinemas) {
|
||||||
|
foreach ($cinemas as $cinema) {
|
||||||
|
$user->cinemas()->attach($cinema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('manage.users');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
$user = \App\Models\User::findOrfail($id);
|
||||||
|
$user->delete();
|
||||||
|
return redirect()->route('manage.users');
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ class Kernel extends HttpKernel
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
'atleast' => \App\Http\Middleware\AtleastRole::class,
|
'atleast' => \App\Http\Middleware\AtleastRole::class,
|
||||||
|
'permission' => \App\Http\Middleware\Permission::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
|
33
app/Http/Middleware/CinemaAccess.php
Normal file
33
app/Http/Middleware/CinemaAccess.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CinemaAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next, $cinema_id)
|
||||||
|
{
|
||||||
|
// does the user have access to the cinema (or is admin)
|
||||||
|
|
||||||
|
if (auth()->user()->atleast('admin')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth()->user()->cinemas->contains($cinema_id)) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
28
app/Http/Middleware/Permission.php
Normal file
28
app/Http/Middleware/Permission.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Permission
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||||
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next, $permission)
|
||||||
|
{
|
||||||
|
// if user.role is admin, allow
|
||||||
|
if ($request->user()->role == 'admin') {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
if (auth()->user()->hasPermission($permission)) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
abort(403, "You need \"$permission\" permission");
|
||||||
|
}
|
||||||
|
}
|
53
app/Models/Cinema.php
Normal file
53
app/Models/Cinema.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Cinema extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'cinemas';
|
||||||
|
protected $primaryKey = 'cinema_id';
|
||||||
|
public $timestamps = false;
|
||||||
|
protected $fillable = [
|
||||||
|
'cinema_name',
|
||||||
|
'address_id',
|
||||||
|
'user_id', // who created this cinema
|
||||||
|
'cinema_open',
|
||||||
|
'cinema_close',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $hidden = [
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function address()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Address::class, 'address_id', 'address_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rooms()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Room::class, 'cinema_id', 'cinema_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showings()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Showing::class, Room::class, 'cinema_id', 'room_id', 'cinema_id', 'room_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->where('cinema_id', $id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function users() {
|
||||||
|
//users associated
|
||||||
|
return $this->belongsToMany('App\Models\User', 'user_assignments', 'cinema_id', 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,4 +22,16 @@ public function users()
|
||||||
return $this->belongsToMany('App\Models\User', 'user_permissions', 'permission_id', 'user_id');
|
return $this->belongsToMany('App\Models\User', 'user_permissions', 'permission_id', 'user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function find(mixed $permission_id)
|
||||||
|
{
|
||||||
|
return $this->where('permission_id', $permission_id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(array $array)
|
||||||
|
{
|
||||||
|
$this->permission_name = $array['permission_name'];
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,17 @@ public function movie()
|
||||||
return $this->belongsTo(Movie::class, 'movie_id', 'movie_id');
|
return $this->belongsTo(Movie::class, 'movie_id', 'movie_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function prices()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Price::class, 'showing_id', 'showing_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function nowPlaying()
|
public function nowPlaying()
|
||||||
{
|
{
|
||||||
return $this->where('showing_start', '>=', now())->get();
|
return $this->where('showing_start', '>=', now())->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function end_time() {
|
public function showing_end() {
|
||||||
$date = new Carbon($this->showing_start);
|
$date = new Carbon($this->showing_start);
|
||||||
$date->addMinutes($this->movie->movie_length);
|
$date->addMinutes($this->movie->movie_length);
|
||||||
return $date;
|
return $date;
|
||||||
|
|
|
@ -54,14 +54,17 @@ public function permissions()
|
||||||
// the permissions are in the permissions table
|
// the permissions are in the permissions table
|
||||||
// only return valid permissions
|
// only return valid permissions
|
||||||
return $this->belongsToMany('App\Models\Permission', 'user_permissions', 'user_id', 'permission_id')->where(function ($query) {
|
return $this->belongsToMany('App\Models\Permission', 'user_permissions', 'user_id', 'permission_id')->where(function ($query) {
|
||||||
$query->where('user_permission_start', '<=', now())->where(function ($query) {
|
// $query->where('user_permission_start', '<=', now())->where(function ($query) {
|
||||||
$query->where('user_permission_end', '>=', now())->orWhereNull('user_permission_end');
|
// $query->where('user_permission_end', '>=', now())->orWhereNull('user_permission_end');
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function allowedTo($permission): bool
|
public function hasPermission($permission): bool
|
||||||
{
|
{
|
||||||
|
if ($this->role == 'admin') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if ($this->permissions()->where('permission_name', $permission)->first()) {
|
if ($this->permissions()->where('permission_name', $permission)->first()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +76,8 @@ public function atleast($role): bool
|
||||||
$hierarchy = [
|
$hierarchy = [
|
||||||
'default' => 0,
|
'default' => 0,
|
||||||
'employee' => 1,
|
'employee' => 1,
|
||||||
'manage' => 2
|
'manage' => 2,
|
||||||
|
'admin' => 3
|
||||||
];
|
];
|
||||||
return $hierarchy[$this->role] >= $hierarchy[$role];
|
return $hierarchy[$this->role] >= $hierarchy[$role];
|
||||||
}
|
}
|
||||||
|
@ -83,4 +87,21 @@ public static function find($id)
|
||||||
return User::all()->where('user_id', $id)->first();
|
return User::all()->where('user_id', $id)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cinemas()
|
||||||
|
{
|
||||||
|
// check user_assignments table for all assignments for this user
|
||||||
|
if ($this->atleast('admin')) {
|
||||||
|
// if user is a manager or admin, return all cinemas
|
||||||
|
return Cinema::all();
|
||||||
|
} else {
|
||||||
|
// if user is an employee, return only the cinemas they are assigned to
|
||||||
|
return $this->belongsToMany('App\Models\Cinema', 'user_assignments', 'user_id', 'cinema_id')->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function orders()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\Order', 'user_id', 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public function up()
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
$table->string('password');
|
$table->string('password');
|
||||||
$table->enum('role', ['default', 'employee', 'manage'])->default('default');
|
$table->enum('role', ['default', 'employee', 'manage', 'admin'])->default('default');
|
||||||
$table->rememberToken();
|
$table->rememberToken();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,8 +19,8 @@ public function up()
|
||||||
$table->foreignId('permission_id')->constrained('permissions', 'permission_id');
|
$table->foreignId('permission_id')->constrained('permissions', 'permission_id');
|
||||||
$table->foreignId('user_id')->constrained('users', 'user_id');
|
$table->foreignId('user_id')->constrained('users', 'user_id');
|
||||||
// end can be null to mark indefinitely
|
// end can be null to mark indefinitely
|
||||||
$table->date('user_permission_start')->useCurrent();
|
// $table->date('user_permission_start')->useCurrent();
|
||||||
$table->date('user_permission_end')->nullable();
|
// $table->date('user_permission_end')->nullable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('user_assignments', function (Blueprint $table) {
|
||||||
|
$table->id('user_assignment_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->foreignId('cinema_id')->constrained('cinemas', 'cinema_id');
|
||||||
|
$table->foreignId('user_id')->constrained('users', 'user_id');
|
||||||
|
$table->unique(['cinema_id', 'user_id']); // only one user per cinema
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('user_assignments');
|
||||||
|
}
|
||||||
|
};
|
|
@ -23,17 +23,14 @@ public function run()
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'room_name' => 'Room 2',
|
'room_name' => 'Room 2',
|
||||||
'room_rows' => 10,
|
'room_rows' => 5,
|
||||||
'room_columns' => 10,
|
'room_columns' => 5,
|
||||||
],
|
|
||||||
[
|
|
||||||
'room_name' => 'Room 3',
|
|
||||||
'room_rows' => 10,
|
|
||||||
'room_columns' => 10,
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($rooms as $room) {
|
foreach ($rooms as $room) {
|
||||||
|
$this->command->info("Creating room {$room['room_name']} for cinema {$cinema->cinema_name}");
|
||||||
|
|
||||||
$r = new \App\Models\Room();
|
$r = new \App\Models\Room();
|
||||||
$r->room_name = $room['room_name'];
|
$r->room_name = $room['room_name'];
|
||||||
$r->room_rows = $room['room_rows'];
|
$r->room_rows = $room['room_rows'];
|
||||||
|
@ -43,6 +40,7 @@ public function run()
|
||||||
$r->save();
|
$r->save();
|
||||||
|
|
||||||
for ($row = 1; $row <= $r->room_rows; $row++) {
|
for ($row = 1; $row <= $r->room_rows; $row++) {
|
||||||
|
$this->command->info("Creating row {$row} for room {$r->room_name}");
|
||||||
for ($column = 1; $column <= $r->room_columns; $column++) {
|
for ($column = 1; $column <= $r->room_columns; $column++) {
|
||||||
$s = new \App\Models\Seat();
|
$s = new \App\Models\Seat();
|
||||||
$s->seat_row = $row;
|
$s->seat_row = $row;
|
||||||
|
|
|
@ -27,22 +27,60 @@ public function run()
|
||||||
'showing_end' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
'showing_end' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
||||||
'movie_id' => 1,
|
'movie_id' => 1,
|
||||||
'room_id' => $room->room_id,
|
'room_id' => $room->room_id,
|
||||||
|
'pricings' => [
|
||||||
|
[
|
||||||
|
'price' => 10,
|
||||||
|
'price_type' => 'adult',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'price' => 8,
|
||||||
|
'price_type' => 'senior',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'price' => 2.5,
|
||||||
|
'price_type' => 'child',
|
||||||
|
],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'showing_start' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
'showing_start' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
||||||
'showing_end' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
'showing_end' => $showStart->add(new \DateInterval('PT2H'))->format('Y-m-d H:i:s'),
|
||||||
'movie_id' => 2,
|
'movie_id' => 2,
|
||||||
'room_id' => $room->room_id,
|
'room_id' => $room->room_id,
|
||||||
|
'pricings' => [
|
||||||
|
[
|
||||||
|
'price' => 10,
|
||||||
|
'price_type' => 'adult',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'price' => 8,
|
||||||
|
'price_type' => 'senior',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'price' => 3.5,
|
||||||
|
'price_type' => 'child',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($showings as $showing) {
|
foreach ($showings as $showing) {
|
||||||
|
$this->command->info("Creating showing for movie {$showing['movie_id']} in room {$room->room_name}");
|
||||||
$s = new \App\Models\Showing();
|
$s = new \App\Models\Showing();
|
||||||
$s->showing_start = $showing['showing_start'];
|
$s->showing_start = $showing['showing_start'];
|
||||||
$s->movie_id = $showing['movie_id'];
|
$s->movie_id = $showing['movie_id'];
|
||||||
$s->room_id = $showing['room_id'];
|
$s->room_id = $showing['room_id'];
|
||||||
$s->user_id = 1;
|
$s->user_id = 1;
|
||||||
$s->save();
|
$s->save();
|
||||||
|
|
||||||
|
foreach ($showing['pricings'] as $pricing) {
|
||||||
|
$p = new \App\Models\Price();
|
||||||
|
$p->price = $pricing['price'];
|
||||||
|
$p->price_type = $pricing['price_type'];
|
||||||
|
$p->showing_id = $s->showing_id;
|
||||||
|
$p->user_id = 1;
|
||||||
|
$p->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,17 @@ version: '3.3'
|
||||||
# LARAVEL ENVIRONMENT COMPOSE
|
# LARAVEL ENVIRONMENT COMPOSE
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:5.7
|
||||||
|
container_name: mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: laravel
|
||||||
|
MYSQL_USER: laravel
|
||||||
|
MYSQL_PASSWORD: laravel
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
phpmyadmin:
|
phpmyadmin:
|
||||||
image: phpmyadmin/phpmyadmin
|
image: phpmyadmin/phpmyadmin
|
||||||
container_name: phpmyadmin
|
container_name: phpmyadmin
|
||||||
|
@ -10,9 +21,8 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
environment:
|
environment:
|
||||||
PMA_HOST: neo.faulty.nl
|
PMA_HOST: mysql
|
||||||
PMA_PORT: 33061
|
PMA_USER: root
|
||||||
PMA_USER: laravel
|
PMA_PASSWORD: root
|
||||||
PMA_PASSWORD: laravel
|
|
||||||
PMA_ARBITRARY: 1
|
PMA_ARBITRARY: 1
|
||||||
PMA_ABSOLUTE_URI: http://localhost:8080/
|
PMA_ABSOLUTE_URI: http://localhost:8080/
|
||||||
|
|
|
@ -130,6 +130,19 @@ a.button:hover {
|
||||||
color: var(--secondary-color-text);
|
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 {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -175,3 +188,14 @@ .op0 {
|
||||||
.mono {
|
.mono {
|
||||||
font-family: monospace;
|
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 @@ #movies a .details {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
max-width: calc(100% - 2rem);
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#movies a .details span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
#movies a:hover .details {
|
#movies a:hover .details {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,14 +46,18 @@ #sidebar a {
|
||||||
color: var(--default-text);
|
color: var(--default-text);
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
|
transition: all 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar a:hover {
|
#sidebar a:hover {
|
||||||
filter: brightness(1.5);
|
background: var(--second-bg);
|
||||||
|
color: var(--default-text);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar a.active {
|
#sidebar a.active {
|
||||||
background: var(--highlight-bg);
|
border-left: 0.5rem solid var(--highlight-bg);
|
||||||
|
background-color: var(--default-bg);
|
||||||
color: var(--highlight-text);
|
color: var(--highlight-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +65,11 @@ #sidebar a.child {
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
/* nice visual*/
|
/* nice visual*/
|
||||||
border-left: 1rem solid var(--second-bg);
|
border-left: 0.5rem solid var(--second-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar a.child.active {
|
||||||
|
border-left: 1rem solid var(--highlight-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
@ -135,10 +143,10 @@ .form .form-group label {
|
||||||
margin: 0.5rem 0;
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .form-group button,
|
.form .form-group button,
|
||||||
form .form-group select,
|
.form .form-group select,
|
||||||
form .form-group textarea,
|
.form .form-group textarea,
|
||||||
form .form-group input {
|
.form .form-group input {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
background: var(--default-bg);
|
background: var(--default-bg);
|
||||||
|
@ -147,14 +155,14 @@ form .form-group input {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .form-group button:hover,
|
.form .form-group button:hover,
|
||||||
form .form-group select:hover,
|
.form .form-group select:hover,
|
||||||
form .form-group textarea:hover,
|
.form .form-group textarea:hover,
|
||||||
form .form-group input:hover,
|
.form .form-group input:hover,
|
||||||
form .form-group button:focus,
|
.form .form-group button:focus,
|
||||||
form .form-group select:focus,
|
.form .form-group select:focus,
|
||||||
form .form-group textarea:focus,
|
.form .form-group textarea:focus,
|
||||||
form .form-group input:focus {
|
.form .form-group input:focus {
|
||||||
filter: brightness(1.5);
|
filter: brightness(1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,3 +197,15 @@ #showings .showing .actions {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: 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 @@ #seat-chooser {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
--seat-width: 2.5rem;
|
--seat-width: 2.5rem;
|
||||||
--seat-height: var(--seat-width);
|
--seat-height: var(--seat-width);
|
||||||
|
@ -95,24 +93,7 @@ #seat-chooser .row .seat:hover {
|
||||||
filter: brightness(0.8);
|
filter: brightness(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
#load-screen {
|
#seat-chooser #seat-chooser-status {
|
||||||
position: fixed;
|
text-align: center;
|
||||||
top: 0;
|
margin: 1rem;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
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';
|
status.style.textAlign = 'center';
|
||||||
this.container.appendChild(status);
|
this.container.appendChild(status);
|
||||||
}
|
}
|
||||||
// set the message
|
// set the message if it exists
|
||||||
|
if (this.container.querySelector('#seat-chooser-status-message'))
|
||||||
this.container.querySelector('#seat-chooser-status-message').innerText = msg;
|
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.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.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.selectedSeats = []; // array of Seat objects
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
|
@ -171,6 +173,7 @@ class SeatChooser {
|
||||||
});
|
});
|
||||||
buttonContainer.appendChild(clearButton);
|
buttonContainer.appendChild(clearButton);
|
||||||
|
|
||||||
|
if (this.submitCallback !== null) {
|
||||||
// submit button
|
// submit button
|
||||||
let submitButton = document.createElement('button');
|
let submitButton = document.createElement('button');
|
||||||
submitButton.classList.add('button');
|
submitButton.classList.add('button');
|
||||||
|
@ -180,6 +183,7 @@ class SeatChooser {
|
||||||
this.submitCallback(this);
|
this.submitCallback(this);
|
||||||
});
|
});
|
||||||
buttonContainer.appendChild(submitButton);
|
buttonContainer.appendChild(submitButton);
|
||||||
|
}
|
||||||
this.container.appendChild(buttonContainer);
|
this.container.appendChild(buttonContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +213,7 @@ class SeatChooser {
|
||||||
this.selectedSeats.push(seat);
|
this.selectedSeats.push(seat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.seatClickCallback(this, seat, this.selectedSeats.includes(seat));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
|
@ -219,21 +224,3 @@ class SeatChooser {
|
||||||
this.selectedSeats = [];
|
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);
|
|
||||||
});
|
|
||||||
|
|
22
resources/views/components/orders.blade.php
Normal file
22
resources/views/components/orders.blade.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Order ID</th>
|
||||||
|
<th>Order Date</th>
|
||||||
|
<th>Order Status</th>
|
||||||
|
<th>Order Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($orders as $order)
|
||||||
|
<tr>
|
||||||
|
<td><a href="/order/{{ $order->order_id }}">{{ $order->order_id }}</a></td>
|
||||||
|
<td>{{ $order->order_date }}</td>
|
||||||
|
<td>{{ $order->order_status }}</td>
|
||||||
|
<td>{{ $order->order_total }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -3,7 +3,15 @@
|
||||||
@push('head')
|
@push('head')
|
||||||
<link rel="stylesheet" href="{{ asset('css/generic.css') }}">
|
<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') }}"></script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
<div id="seat-chooser"></div>
|
<div>
|
||||||
|
|
||||||
|
<div id="seat-chooser" data-showing-id="{{ $showing_id }}">
|
||||||
|
<div id="seat-chooser-status">
|
||||||
|
<h2>Loading...</h2>
|
||||||
|
<span id="seat-chooser-status-message">Fetching prices...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
<hr/>
|
<hr/>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
<h2>Orders</h2>
|
||||||
|
<hr/>
|
||||||
|
<x-orders :orders="Auth()->user()->orders" />
|
||||||
|
|
||||||
<h2>Permissions</h2>
|
<h2>Permissions</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{{ isset($title) ? $title : 'Page' }} - {{ config('app.name', 'CineFlex') }} Manage</title>
|
<title>{{ isset($title) ? $title : 'Page' }} - {{ config('app.name', 'CineFlex') }} Manage</title>
|
||||||
|
<script src="{{asset('js/freq.js')}}"></script>
|
||||||
@stack('head')
|
@stack('head')
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
<ul>
|
<ul>
|
||||||
@foreach($cinemas as $cinema)
|
@foreach($cinemas as $cinema)
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ route('cinema', ['id' => $cinema->id]) }}">
|
<a href="{{ route('cinema', ['id' => $cinema->cinema_id]) }}">
|
||||||
{{ $cinema->name }}
|
{{ $cinema->cinema_name }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
|
@ -10,8 +10,15 @@
|
||||||
<h1>Now playing:</h1>
|
<h1>Now playing:</h1>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div id="movies">
|
<div id="movies">
|
||||||
@foreach($movies as $showing)
|
@foreach($movies as $movie)
|
||||||
|
<a class="movie" href="{{route('movie', ['id' => $movie->movie_id])}}">
|
||||||
|
<img src="{{ $movie->movie_image }}" alt="{{ $movie->movie_name }}">
|
||||||
|
<div class="details">
|
||||||
|
<h2>{{ $movie->movie_name }}</h2>
|
||||||
|
<span>{{ $movie->movie_description }}</span>
|
||||||
|
<p>{{ $movie->movie_length }} minutes</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
29
resources/views/main/movies/movie.blade.php
Normal file
29
resources/views/main/movies/movie.blade.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@extends('main.layout')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h1>{{$movie->movie_name}}</h1>
|
||||||
|
<hr/>
|
||||||
|
<div id="movie">
|
||||||
|
<img src="{{$movie->movie_image}}" alt="{{$movie->movie_name}} Poster" width="200px">
|
||||||
|
<div class="details">
|
||||||
|
<span>{{$movie->movie_length}} min</span><br/>
|
||||||
|
<span>{{$movie->movie_description}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h1>Now playing:</h1>
|
||||||
|
<hr/>
|
||||||
|
<div id="showings">
|
||||||
|
@foreach($movie->showings as $showing)
|
||||||
|
<a href="/showing/{{$showing->showing_id}}">
|
||||||
|
<h2>{{$showing->room->cinema->cinema_name}} @ {{$showing->showing_start}} - {{$showing->showing_end()}}</h2>
|
||||||
|
</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@endsection
|
35
resources/views/main/order.blade.php
Normal file
35
resources/views/main/order.blade.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@extends('layout')
|
||||||
|
|
||||||
|
@push('head')
|
||||||
|
<link rel="stylesheet" href="{{asset('css/generic.css')}}">
|
||||||
|
<link rel="stylesheet" href="{{asset('css/order.css')}}">
|
||||||
|
<script src="{{asset('js/order.js')}}" defer></script>
|
||||||
|
@endpush
|
||||||
|
|
||||||
|
@section('body')
|
||||||
|
<h1>Ordering tickets for {{ $showing->movie->movie_name }}</h1>
|
||||||
|
<hr/>
|
||||||
|
<div id="showing">
|
||||||
|
<div class="details">
|
||||||
|
<span>Cinema: <a
|
||||||
|
href="/cinema/{{$showing->room->cinema->cinema_id}}">{{$showing->room->cinema->cinema_name}}</a></span><br/>
|
||||||
|
<span>Room: {{$showing->room->room_name}}</span><br/>
|
||||||
|
<span>Showing: {{$showing->showing_start}} - {{$showing->showing_end()}}</span><br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
@component('components.seat-chooser', ['room_id' => $showing->room->room_id, 'showing_id' => $showing->showing_id])
|
||||||
|
@endcomponent
|
||||||
|
<hr/>
|
||||||
|
<div id="summary">
|
||||||
|
<h2>Summary</h2>
|
||||||
|
<hr/>
|
||||||
|
<div id="summary-dyn">
|
||||||
|
No seats selected.
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div id="summary-total">Total: €0.00</div>
|
||||||
|
</div>
|
||||||
|
<button id="order-button" class="button" disabled=true>Order</button>
|
||||||
|
|
||||||
|
@endsection
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<span>Cinema: <a
|
<span>Cinema: <a
|
||||||
href="/cinema/{{$showing->room->cinema->cinema_id}}">{{$showing->room->cinema->cinema_name}}</a></span><br/>
|
href="/cinema/{{$showing->room->cinema->cinema_id}}">{{$showing->room->cinema->cinema_name}}</a></span><br/>
|
||||||
<span>Showing: {{$showing->showing_start}} - {{$showing->end_time()}}</span><br/>
|
<span>Showing: {{$showing->showing_start}} - {{$showing->showing_end()}}</span><br/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -30,8 +30,7 @@
|
||||||
<section>
|
<section>
|
||||||
<h1>Tickets:</h1>
|
<h1>Tickets:</h1>
|
||||||
<hr/>
|
<hr/>
|
||||||
<span>Tickets available: {{$showing->tickets_available()}}</span><br/>
|
<span>Tickets available: {{$showing->tickets_available()}}</span><br/><br/>
|
||||||
<span>Buy tickets?</span>
|
<a href="/showing/{{$showing->showing_id}}/order" class="button">Order Tickets</a>
|
||||||
{{-- todo--}}
|
|
||||||
</section>
|
</section>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -3,46 +3,27 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
<h1>Cinemas</h1>
|
<h1>Cinemas</h1>
|
||||||
<hr/><br/>
|
<hr/><br/>
|
||||||
<h2>Stats:</h2>
|
<h2>Cinemas ({{ count(Auth::user()->cinemas()) }} / {{ $cinemas->count() }})</h2>
|
||||||
<div class="spread-h">
|
<div>
|
||||||
<a class="clicky-card" href="{{ route('manage.cinemas') }}">
|
@if (count(Auth::user()->cinemas()) > 0)
|
||||||
<div class="card">
|
<ul>
|
||||||
<h3>Amount Cinemas</h3>
|
@foreach(Auth::user()->cinemas() as $cinema)
|
||||||
<span class="big-stat">{{ $cinemas->count() }}</span>
|
<li>
|
||||||
</div>
|
<a href="{{ route('manage.cinema', ['id' => $cinema->cinema_id]) }}">
|
||||||
</a>
|
<div>
|
||||||
<a class="clicky-card" href="{{ route('manage.cinemas.employees') }}">
|
<h3>{{ $cinema->cinema_name }}</h3>
|
||||||
<div class="card">
|
|
||||||
<h3>Amount Employees</h3>
|
|
||||||
<span class="big-stat">{{ $employees->count() }}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<h2>Cinemas:</h2>
|
|
||||||
<div class="spread-h">
|
|
||||||
@foreach($cinemas as $cinema)
|
|
||||||
<a class="clicky-card" href="{{ route('manage.cinemas.show', ['id' => $cinema->id]) }}">
|
|
||||||
<div class="card">
|
|
||||||
<h3>{{ $cinema->name }}</h3>
|
|
||||||
<span class="big-stat">{{ $cinema->address->address_city }}</span>
|
<span class="big-stat">{{ $cinema->address->address_city }}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
@else
|
||||||
|
<div class="warning">
|
||||||
|
<h3>No Cinemas</h3>
|
||||||
|
<span>Did you have permission?</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<h2>Employees:</h2>
|
|
||||||
<div class="spread-h">
|
|
||||||
@foreach($employees as $employee)
|
|
||||||
<a class="clicky-card" href="{{ route('manage.cinemas.employees.show', ['id' => $employee->id]) }}">
|
|
||||||
<div class="card">
|
|
||||||
<h3>{{ $employee->name }}</h3>
|
|
||||||
<span class="big-stat">{{ $employee->cinema->name }}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<a class="btn btn-primary" href="{{ route('manage') }}">Back</a>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<hr/><br/>
|
<hr/><br/>
|
||||||
<h2>Stats:</h2>
|
<h2>Stats:</h2>
|
||||||
<div class="spread-h">
|
<div class="spread-h">
|
||||||
<a class="clicky-card" href="{{ route('manage') }}">
|
<a class="clicky-card" href="{{ route('manage.users') }}">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>Amount Users</h3>
|
<h3>Amount Users</h3>
|
||||||
<span class="big-stat">{{ $users->count() }}</span>
|
<span class="big-stat">{{ $users->count() }}</span>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<span class="big-stat">{{ $genres->count() }}</span>
|
<span class="big-stat">{{ $genres->count() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="clicky-card" href="{{ route('manage') }}">
|
<a class="clicky-card" href="{{ route('manage.showings') }}">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>Amount Showings</h3>
|
<h3>Amount Showings</h3>
|
||||||
<span class="big-stat">{{ $showings->count() }}</span>
|
<span class="big-stat">{{ $showings->count() }}</span>
|
||||||
|
|
|
@ -18,12 +18,26 @@
|
||||||
</header>
|
</header>
|
||||||
<div id="sidebar"> {{-- Page Aware --}}
|
<div id="sidebar"> {{-- Page Aware --}}
|
||||||
<a class="{{ Request::is('manage') ? 'active' : '' }}" href="{{ route('manage') }}">Dashboard</a>
|
<a class="{{ Request::is('manage') ? 'active' : '' }}" href="{{ route('manage') }}">Dashboard</a>
|
||||||
{{-- manage cinemas--}}
|
|
||||||
{{-- <a class="{{ Request::is('manage/cinemas') ? 'active' : '' }}" href="{{ route('manage.cinemas') }}">Cinemas</a>--}}
|
@if (Auth::user()->hasPermission("READ_CINEMAS"))
|
||||||
{{-- <a class="{{ Request::is('manage/cinemas/employees') ? 'active' : '' }} child" href="{{ route('manage.cinemas.employees') }}">Employees</a>--}}
|
<a class="{{ Request::is('manage/cinemas') ? 'active' : '' }}" href="{{ route('manage.cinemas') }}">Cinemas</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (Auth::user()->hasPermission("READ_MOVIES"))
|
||||||
<a class="{{ Request::is('manage/movies') ? 'active' : '' }}" href="{{ route('manage.movies') }}">Movies</a>
|
<a class="{{ Request::is('manage/movies') ? 'active' : '' }}" href="{{ route('manage.movies') }}">Movies</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (Auth::user()->hasPermission("READ_SHOWINGS"))
|
||||||
<a class="child {{ Request::is('manage/showings') ? 'active' : '' }}" href="{{ route('manage.showings') }}">Showings</a>
|
<a class="child {{ Request::is('manage/showings') ? 'active' : '' }}" href="{{ route('manage.showings') }}">Showings</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (Auth::user()->hasPermission("READ_GENRES"))
|
||||||
<a class="{{ Request::is('manage/genres') ? 'active' : '' }}" href="{{ route('manage.genres') }}">Genres</a>
|
<a class="{{ Request::is('manage/genres') ? 'active' : '' }}" href="{{ route('manage.genres') }}">Genres</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(Auth::user()->atleast('manage') && Auth::user()->hasPermission("READ_USERS"))
|
||||||
|
<a class="{{ Request::is('manage/users') ? 'active' : '' }}" href="{{ route('manage.users') }}">Users</a>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
@yield('content')
|
@yield('content')
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<h3>Times</h3>
|
<h3>Times</h3>
|
||||||
<span class="mono">
|
<span class="mono">
|
||||||
Start: {{ $showing->showing_start }}<br>
|
Start: {{ $showing->showing_start }}<br>
|
||||||
Ends : {{ $showing->end_time() }}
|
Ends : {{ $showing->showing_end() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
|
41
resources/views/manage/users/index.blade.php
Normal file
41
resources/views/manage/users/index.blade.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
@extends('manage.layout')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<h1>Users</h1>
|
||||||
|
<hr/>
|
||||||
|
<div id="users">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
@foreach($users as $user)
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('manage.user', ['id' => $user->user_id]) }}">
|
||||||
|
{{ $user->name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('manage.user', ['id' => $user->user_id]) }}">
|
||||||
|
{{ $user->email }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('manage.user', ['id' => $user->user_id]) }}">
|
||||||
|
{{ $user->role }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('manage.user', ['id' => $user->user_id]) }}">
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endsection
|
117
resources/views/manage/users/user.blade.php
Normal file
117
resources/views/manage/users/user.blade.php
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
@extends('manage.layout')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<h1>User: {{ $user->name }}</h1>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<h2>Core User Details</h2>
|
||||||
|
<form id="core-user-form" class="form" method="POST" action="{{ route('manage.user', ['id' => $user->user_id ]) }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" name="name" id="name" value="{{ $user->name }}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" placeholder="Change password" name="password" id="password"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="role">Role</label>
|
||||||
|
<select>
|
||||||
|
<option value="admin" {{ $user->role == 'admin' ? 'selected' : '' }}>Admin</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script id="core-user-form-loader">
|
||||||
|
// request allowed roles to give from the API (/api/allowedRoles)
|
||||||
|
// populate the select with the allowed roles
|
||||||
|
// set the selected role to the current role
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2>User Permissions</h2>
|
||||||
|
{{-- for loop, and make sure that only if the user has the DELETE_PERMISSION that they can delete it and if they have the UPDATE_PERMISSION that they can update it--}}
|
||||||
|
<div id="permissions">
|
||||||
|
<table id="permissions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Permission</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
@foreach(auth()->user()->permissions as $permission)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $permission->permission_name }}</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ route('api.user.permission', ['id' => $user->user_id, 'permission_id' => $permission->permission_id]) }}" method="DELETE">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<button type="submit">Delete</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
<form id="add-permission" method="POST"
|
||||||
|
action="{{ route('api.user.permission', ['id' => $user->user_id ]) }}">
|
||||||
|
@csrf
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input class="text" type="text" name="permission_name" id="permission"/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="button" type="submit">Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Cinema Assignments</h2>
|
||||||
|
<table id="assignments">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Assignment</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
@foreach(auth()->user()->cinemas() as $cinema)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $cinema->cinema_name }}</td>
|
||||||
|
<td>
|
||||||
|
@if($user->hasPermission('DELETE_CINEMA_ASSIGNMENT'))
|
||||||
|
<a href="{{ route('api.user.assignment', ['id' => $cinema->cinema_id]) }}">
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@if ($user->hasPermission('UPDATE_USER'))
|
||||||
|
<form id="add-assignment" method="POST"
|
||||||
|
action="{{ route('api.user.assignment', ['id' => $user->user_id ]) }}">
|
||||||
|
@csrf
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<select name="cinema" id="cinema">
|
||||||
|
@foreach(auth()->user()->cinemas() as $cinema)
|
||||||
|
<option value="{{ $cinema->cinema_id }}">{{ $cinema->cinema_name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="button" type="submit">Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
||||||
return $request->user();
|
return $request->user();
|
||||||
});
|
});
|
||||||
|
@ -42,3 +43,72 @@
|
||||||
$showing = (new App\Models\Showing)->find($id);
|
$showing = (new App\Models\Showing)->find($id);
|
||||||
return $showing->seatMatrix();
|
return $showing->seatMatrix();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// /api/showing/{id}/prices
|
||||||
|
Route::get('/showing/{id}/prices', function ($id) {
|
||||||
|
$showing = (new App\Models\Showing)->find($id);
|
||||||
|
return $showing->prices;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// /api/user/{id}/permissions
|
||||||
|
Route::get('/user/{id}/permissions', function ($id) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
return $user->permissions;
|
||||||
|
})->name('api.user.permissions');
|
||||||
|
|
||||||
|
Route::post('/user/{id}/permission', function ($id, Request $request) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
|
||||||
|
// check if request is valid
|
||||||
|
if (!isset($request->permission_name)) {
|
||||||
|
return response()->json(['error' => 'permission name not set'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if permission exists
|
||||||
|
// if it does not exist, create it
|
||||||
|
$permission = (new App\Models\Permission)->where('permission_name', $request->permission_name);
|
||||||
|
if ($permission->count() == 0) {
|
||||||
|
$permission = (new App\Models\Permission)->create(['permission_name' => $request->permission_name]);
|
||||||
|
} else {
|
||||||
|
$permission = $permission->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if user has permission
|
||||||
|
// if not, add it
|
||||||
|
|
||||||
|
$user->permissions()->attach($permission->permission_id);
|
||||||
|
$user->save();
|
||||||
|
return $user->permissions;
|
||||||
|
|
||||||
|
})->name('api.user.permission');
|
||||||
|
|
||||||
|
Route::delete('/user/{id}/permission', function ($id, Request $request) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
$perm = (new App\Models\Permission())->find(request('permission_id'));
|
||||||
|
$user->permissions->detach($perm);
|
||||||
|
$user->save();
|
||||||
|
return $user->permissions;
|
||||||
|
})->name('api.user.permission');
|
||||||
|
|
||||||
|
// /api/user/{id}/assignments
|
||||||
|
Route::get('/user/{id}/assignments', function ($id) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
return $user->cinemas;
|
||||||
|
})->name('api.user.assignments');
|
||||||
|
|
||||||
|
Route::post('/user/{id}/assignment', function ($id) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
$cinema = (new App\Models\Cinema())->find(request('cinema_id'));
|
||||||
|
$user->cinemas->attach($cinema);
|
||||||
|
$user->save();
|
||||||
|
return $user->cinemas;
|
||||||
|
})->name('api.user.assignment');
|
||||||
|
|
||||||
|
Route::delete('/user/{id}/assignment', function ($id) {
|
||||||
|
$user = (new App\Models\User)->find($id);
|
||||||
|
$cinema = (new App\Models\Cinema())->find(request('cinema_id'));
|
||||||
|
$user->cinemas->detach($cinema);
|
||||||
|
$user->save();
|
||||||
|
return $user->cinemas;
|
||||||
|
})->name('api.user.assignment');
|
||||||
|
|
|
@ -46,8 +46,9 @@
|
||||||
|
|
||||||
Route::get('/showings', [App\Http\Controllers\Main\ShowingController::class, 'showAllShowings'])->name('showings');
|
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('/showing/{id}', [App\Http\Controllers\Main\ShowingController::class, 'show'])->name('showing');
|
||||||
|
Route::get('/showing/{id}/order', [App\Http\Controllers\Main\ShowingController::class, 'order'])->name('showing.order');
|
||||||
|
|
||||||
Route::get('/movies', [App\Http\Controllers\Main\MovieController::class, 'showAllMovies'])->name('movies');
|
Route::get('/movies', [App\Http\Controllers\Main\MovieController::class, 'moviesNowShowing'])->name('movies');
|
||||||
Route::get('/movie/{id}', [App\Http\Controllers\Main\MovieController::class, 'show'])->name('movie');
|
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('/genres', [App\Http\Controllers\Main\GenreController::class, 'showAllGenres'])->name('genres');
|
||||||
|
@ -69,12 +70,11 @@
|
||||||
})->name('manage');
|
})->name('manage');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::controller(\App\Http\Controllers\Managing\MovieController::class)->group(function () {
|
Route::controller(\App\Http\Controllers\Managing\MovieController::class)->group(function () {
|
||||||
|
|
||||||
|
|
||||||
// /manage/movies - CR movies (FB)
|
// /manage/movies - CR movies (FB)
|
||||||
Route::get('/manage/movies', [\App\Http\Controllers\Managing\MovieController::class, 'showAllMovies'])->name('manage.movies');
|
Route::get('/manage/movies', [\App\Http\Controllers\Managing\MovieController::class, 'index'])->name('manage.movies');
|
||||||
Route::post('/manage/movies', [\App\Http\Controllers\Managing\MovieController::class, 'store'])->name('manage.movies');
|
Route::post('/manage/movies', [\App\Http\Controllers\Managing\MovieController::class, 'store'])->name('manage.movies');
|
||||||
|
|
||||||
// /manage/movies/create - C movie (F)
|
// /manage/movies/create - C movie (F)
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
|
|
||||||
Route::controller(\App\Http\Controllers\Managing\GenreController::class)->group(function () {
|
Route::controller(\App\Http\Controllers\Managing\GenreController::class)->group(function () {
|
||||||
// /manage/genres - CR genres (FB)
|
// /manage/genres - CR genres (FB)
|
||||||
Route::get('/manage/genres', [\App\Http\Controllers\Managing\GenreController::class, 'showAllGenres'])->name('manage.genres');
|
Route::get('/manage/genres', [\App\Http\Controllers\Managing\GenreController::class, 'index'])->name('manage.genres');
|
||||||
Route::post('/manage/genres', [\App\Http\Controllers\Managing\GenreController::class, 'store'])->name('manage.genres');
|
Route::post('/manage/genres', [\App\Http\Controllers\Managing\GenreController::class, 'store'])->name('manage.genres');
|
||||||
|
|
||||||
// /manage/genres/create - C genre (F)
|
// /manage/genres/create - C genre (F)
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
|
|
||||||
Route::controller(\App\Http\Controllers\Managing\CinemaController::class)->group(function () {
|
Route::controller(\App\Http\Controllers\Managing\CinemaController::class)->group(function () {
|
||||||
// /manage/cinemas - CR cinemas (FB)
|
// /manage/cinemas - CR cinemas (FB)
|
||||||
Route::get('/manage/cinemas', [\App\Http\Controllers\Managing\CinemaController::class, 'showAllCinemas'])->name('manage.cinemas');
|
Route::get('/manage/cinemas', [\App\Http\Controllers\Managing\CinemaController::class, 'index'])->name('manage.cinemas');
|
||||||
Route::post('/manage/cinemas', [\App\Http\Controllers\Managing\CinemaController::class, 'store'])->name('manage.cinemas');
|
Route::post('/manage/cinemas', [\App\Http\Controllers\Managing\CinemaController::class, 'store'])->name('manage.cinemas');
|
||||||
|
|
||||||
// /manage/cinemas/create - C cinema (F)
|
// /manage/cinemas/create - C cinema (F)
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
|
|
||||||
Route::controller(\App\Http\Controllers\Managing\ShowingsController::class)->group(function () {
|
Route::controller(\App\Http\Controllers\Managing\ShowingsController::class)->group(function () {
|
||||||
// /manage/showings - CR showings (FB)
|
// /manage/showings - CR showings (FB)
|
||||||
Route::get('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'showAllShowings'])->name('manage.showings');
|
Route::get('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'index'])->name('manage.showings');
|
||||||
Route::post('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'store'])->name('manage.showings');
|
Route::post('/manage/showings', [\App\Http\Controllers\Managing\ShowingsController::class, 'store'])->name('manage.showings');
|
||||||
|
|
||||||
// /manage/showings/create - C showing (F)
|
// /manage/showings/create - C showing (F)
|
||||||
|
@ -132,13 +132,18 @@
|
||||||
Route::delete('/manage/showing/{id}', [\App\Http\Controllers\Managing\ShowingsController::class, 'destroy'])->name('manage.showing');
|
Route::delete('/manage/showing/{id}', [\App\Http\Controllers\Managing\ShowingsController::class, 'destroy'])->name('manage.showing');
|
||||||
});
|
});
|
||||||
|
|
||||||
// /test/comp/{component}
|
Route::controller(\App\Http\Controllers\Managing\UsersController::class)->group(function () {
|
||||||
Route::get('/test/comp/{component}', function ($component) {
|
// /manage/users - CR users (FB)
|
||||||
switch ($component) {
|
Route::get('/manage/users', [\App\Http\Controllers\Managing\UsersController::class, 'index'])->name('manage.users');
|
||||||
case 'seat-chooser':
|
Route::post('/manage/users', [\App\Http\Controllers\Managing\UsersController::class, 'store'])->name('manage.users');
|
||||||
$c = new \App\View\Components\SeatChooser(1, 1);
|
|
||||||
return $c->render();
|
// /manage/users/create - C user (F)
|
||||||
default:
|
Route::get('/manage/users/create', [\App\Http\Controllers\Managing\UsersController::class, 'createUser'])->name('manage.users.create');
|
||||||
return "No component found";
|
// no post, handled by POST /users
|
||||||
}
|
|
||||||
})->name('test.comp');
|
// /manage/users/{id} - RUD user (FB)
|
||||||
|
Route::get('/manage/user/{id}', [\App\Http\Controllers\Managing\UsersController::class, 'show'])->name('manage.user');
|
||||||
|
Route::put('/manage/user/{id}', [\App\Http\Controllers\Managing\UsersController::class, 'update'])->name('manage.user');
|
||||||
|
Route::delete('/manage/user/{id}', [\App\Http\Controllers\Managing\UsersController::class, 'destroy'])->name('manage.user');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
2
storage/debugbar/.gitignore
vendored
Normal file
2
storage/debugbar/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
Loading…
Reference in a new issue