fix: working copy, caching ASP, notworking orders though

This commit is contained in:
Didier Slof 2023-02-03 09:05:29 +01:00
parent dc5d204cfc
commit 390ad23e03
Signed by: didier
GPG key ID: 01E71F18AA4398E5
32 changed files with 246 additions and 194 deletions

View file

@ -19,6 +19,10 @@ public function showAllShowings()
public function order($id) public function order($id)
{ {
// if not authenticated piss off
if (!auth()->check()) {
return redirect()->route('login');
}
return view('main.order', ['title' => "Order Tickets", 'showing' => \App\Models\Showing::findOrfail($id)]); return view('main.order', ['title' => "Order Tickets", 'showing' => \App\Models\Showing::findOrfail($id)]);
} }
} }

View file

@ -104,4 +104,9 @@ public function orders()
return $this->hasMany('App\Models\Order', 'user_id', 'user_id'); return $this->hasMany('App\Models\Order', 'user_id', 'user_id');
} }
public function address()
{
return $this->hasOne('App\Models\Address', 'address_id', 'address_id');
}
} }

View file

@ -2,7 +2,8 @@
namespace App\Providers; namespace App\Providers;
// use Illuminate\Support\Facades\Gate; use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider class AuthServiceProvider extends ServiceProvider
@ -24,7 +25,5 @@ class AuthServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
$this->registerPolicies(); $this->registerPolicies();
//
} }
} }

View file

@ -23,7 +23,7 @@ public function handleCache()
{ {
if (!\Cache::has($this->src)) { if (!\Cache::has($this->src)) {
$image = file_get_contents($this->src); $image = file_get_contents($this->src);
\Cache::put($this->src, $image, 60 * 24 * 7); \Cache::put($this->src, $image, 60 * 24 * 7); // 1 week
} }
return \Cache::get($this->src); return \Cache::get($this->src);
} }

View file

@ -40,6 +40,10 @@
'driver' => 'session', 'driver' => 'session',
'provider' => 'users', 'provider' => 'users',
], ],
'api' => [
'driver' => 'passport',
'provider' => 'users'
]
], ],
/* /*

View file

@ -15,12 +15,12 @@ public function up()
Schema::create('addresses', function (Blueprint $table) { Schema::create('addresses', function (Blueprint $table) {
$table->id('address_id'); $table->id('address_id');
$table->timestamps(); $table->timestamps();
$table->addColumn('string', 'address_street', ['length' => 255]); $table->string('address_street', 255);
$table->addColumn('string', 'address_city', ['length' => 255]); $table->string('address_city', 255);
$table->addColumn('string', 'address_state', ['length' => 255]); $table->string('address_state', 255);
$table->addColumn('string', 'address_zip', ['length' => 255]); $table->string('address_zip', 255);
$table->addColumn('string', 'address_country', ['length' => 255])->default('Netherlands'); $table->string('address_country', 255)->default('Netherlands');
$table->addColumn('string', 'address_phone', ['length' => 255])->nullable(); $table->string('address_phone', 255)->nullable();
}); });
} }

View file

@ -20,6 +20,7 @@ public function up()
$table->timestamp('email_verified_at')->nullable(); $table->timestamp('email_verified_at')->nullable();
$table->string('password'); $table->string('password');
$table->enum('role', ['default', 'employee', 'manage', 'admin'])->default('default'); $table->enum('role', ['default', 'employee', 'manage', 'admin'])->default('default');
$table->foreignId('address_id')->nullable()->constrained('addresses', 'address_id')->onDelete('cascade');
$table->rememberToken(); $table->rememberToken();
$table->timestamps(); $table->timestamps();
}); });

View file

@ -14,7 +14,7 @@
public function up() public function up()
{ {
Schema::create('personal_access_tokens', function (Blueprint $table) { Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id('personal_access_token_id'); $table->id();
$table->morphs('tokenable'); $table->morphs('tokenable');
$table->string('name'); $table->string('name');
$table->string('token', 64)->unique(); $table->string('token', 64)->unique();

View file

@ -18,6 +18,8 @@ services:
image: phpmyadmin/phpmyadmin image: phpmyadmin/phpmyadmin
container_name: phpmyadmin container_name: phpmyadmin
restart: always restart: always
links:
- mysql
ports: ports:
- "8080:80" - "8080:80"
environment: environment:

View file

@ -0,0 +1 @@
@import "../manage/movies.css";

View file

@ -2,8 +2,10 @@ body {
/* center with spacing on the left and right*/ /* center with spacing on the left and right*/
margin: 0 auto; margin: 0 auto;
width: 100%; width: 100%;
max-width: 1000px;
padding: 0 20px 3rem; padding: 0 20px 3rem;
display: grid;
place-content: center;
} }
#seat-chooser { #seat-chooser {
@ -51,3 +53,7 @@ #summary-dyn .seat .price {
margin: 0.5rem; margin: 0.5rem;
float: right; float: right;
} }
#order-button {
margin-bottom: 10rem;
}

View file

@ -0,0 +1,51 @@
.form {
display: flex;
flex-direction: column;
margin: 0.5rem 0;
padding: 0.5rem;
background: var(--second-bg);
border-radius: 0.5rem;
}
.form .form-group {
display: flex;
flex-direction: column;
margin: 0.5rem 0;
}
.form .form-group label {
margin: 0.5rem 0;
}
.form .form-group button,
.form .form-group select,
.form .form-group textarea,
.form .form-group input {
padding: 0.5rem;
border-radius: 0.5rem;
background: var(--default-bg);
color: var(--default-text);
outline: none;
border: none;
}
.form .form-group button:hover,
.form .form-group select:hover,
.form .form-group textarea:hover,
.form .form-group input:hover,
.form .form-group button:focus,
.form .form-group select:focus,
.form .form-group textarea:focus,
.form .form-group input:focus {
filter: brightness(1.5);
}
form .form-group button {
background: var(--highlight-bg);
color: var(--highlight-text);
cursor: pointer;
}
form .form-group button:hover {
filter: brightness(1.5);
}

View file

@ -9,6 +9,7 @@ header {
align-items: center; align-items: center;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
box-shadow: #111111 0 0 10px;
} }
header span { header span {
@ -91,90 +92,9 @@ .card .big-stat {
font-weight: bold; font-weight: bold;
} }
#movies {
display: flex;
flex-direction: column;
}
#movies a {
display: flex;
flex-direction: row;
margin: 0.5rem 0;
padding: 0.5rem;
background: var(--second-bg);
border-radius: 0.5rem;
}
#movies a img {
width: 10vw;
height: 16vw;
object-fit: cover;
transition: transform 0.5s;
}
#movies a:hover {
filter: brightness(1.5);
}
#movies a .details {
display: flex;
flex-direction: column;
margin-left: 1rem;
}
/* form */ /* form */
.form {
display: flex;
flex-direction: column;
margin: 0.5rem 0;
padding: 0.5rem;
background: var(--second-bg);
border-radius: 0.5rem;
}
.form .form-group {
display: flex;
flex-direction: column;
margin: 0.5rem 0;
}
.form .form-group label {
margin: 0.5rem 0;
}
.form .form-group button,
.form .form-group select,
.form .form-group textarea,
.form .form-group input {
padding: 0.5rem;
border-radius: 0.5rem;
background: var(--default-bg);
color: var(--default-text);
outline: none;
border: none;
}
.form .form-group button:hover,
.form .form-group select:hover,
.form .form-group textarea:hover,
.form .form-group input:hover,
.form .form-group button:focus,
.form .form-group select:focus,
.form .form-group textarea:focus,
.form .form-group input:focus {
filter: brightness(1.5);
}
form .form-group button {
background: var(--highlight-bg);
color: var(--highlight-text);
cursor: pointer;
}
form .form-group button:hover {
filter: brightness(1.5);
}
#showings { #showings {
display: flex; display: flex;

View file

@ -0,0 +1,63 @@
#movie-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
flex-direction: row;
gap: 1rem;
}
#movie-grid a {
position: relative;
}
#movie-grid a .details {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
place-content: center;
align-content: center;
background: #000;
opacity: 0;
color: var(--default-text);
transition: all .5s;
max-height: 100%;
max-width: 100%;
}
#movie-grid a:hover .details {
opacity: 0.7;
}
/* --- */
#movie-list {
display: flex;
flex-direction: column;
}
#movie-list a {
display: flex;
flex-direction: row;
margin: 0.5rem 0;
padding: 0.5rem;
background: var(--second-bg);
border-radius: 0.5rem;
}
#movie-list a img {
width: 10vw;
height: 16vw;
object-fit: cover;
transition: transform 0.5s;
}
#movie-list a .details {
display: flex;
flex-direction: column;
margin-left: 1rem;
}

View file

@ -1,31 +0,0 @@
#movies {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
flex-direction: row;
gap: 1rem;
}
/* Show .details on top of image */
#movies a {
position: relative;
}
#movies a .details {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
place-content: center;
align-content: center;
background: var(--second-bg);
opacity: 0;
color: var(--default-text);
transition: all .5s;
}
#movies a:hover .details {
opacity: 0.7;
}

View file

@ -62,7 +62,7 @@
} }
); );
window.freq.set('seat-chooser', sc); window.sc = sc;
})(); })();
let total = 0; let total = 0;
@ -84,13 +84,14 @@ document.querySelector('button#order-button').addEventListener('click', (e) => {
window.sc.selectedSeats.forEach(seat => { window.sc.selectedSeats.forEach(seat => {
seats.push({ seats.push({
seat: seat.id, seat: seat.id,
ticket: seat.ticket.id price: seat.ticket.id,
}); });
}); });
fetch(`/api/order`, { fetch(`/api/order`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + document.querySelector('#order-button').getAttribute('data-token') || ''
}, },
body: JSON.stringify({ body: JSON.stringify({
showing: sc.showingId, showing: sc.showingId,
@ -98,7 +99,8 @@ document.querySelector('button#order-button').addEventListener('click', (e) => {
}) })
}).then(res => { }).then(res => {
if (res.ok) { if (res.ok) {
window.location = '/order/complete'; // window.location = '/order/complete';
console.log('Order placed');
} else { } else {
alert('Error placing order'); alert('Error placing order');
} }

View file

@ -8,7 +8,7 @@
<h2>{{ __('Login') }}</h2> <h2>{{ __('Login') }}</h2>
<div class="form"> <div class="form">
<form method="POST" action="{{ route('login') }}"> <form method="POST" action="{{ route('login', ['redirect' => request()->get('redirect')]) }}">
@csrf @csrf
<div class="item"> <div class="item">
<label for="email">{{ __('E-Mail Address') }}</label><br/> <label for="email">{{ __('E-Mail Address') }}</label><br/>

View file

@ -0,0 +1,12 @@
<div id="movie-grid">
@foreach($movies as $movie)
<a class="movie" href="{{route('movie', ['id' => $movie->movie_id])}}">
<x-cache-image src="{{$movie->movie_image}}" alt="{{$movie->movie_name}} Poster" class="poster" width="200px"/>
<div class="details">
<h2>{{ $movie->movie_name }}</h2>
<span>{{ Str::limit($movie->movie_description, 150) }}</span>
<p>{{ $movie->movie_length }} minutes</p>
</div>
</a>
@endforeach
</div>

View file

@ -0,0 +1,17 @@
@push('head')
<link rel="stylesheet" href="{{ asset('css/manage/movies.css') }}">
@endpush
<div id="movie-list">
@foreach($movies as $movie)
<a href="/manage/movie/{{$movie->movie_id}}">
<x-cache-image src="{{$movie->movie_image}}" alt="{{$movie->movie_name}} Poster" class="poster" width="200px"/>
<div class="details">
<h3>{{$movie->movie_name}}</h3><br/>
<span>{{ Str::limit($movie->movie_description, 500) }}</span><br/>
<hr/>
<span>{{$movie->movie_length}} min | {{$movie->movie_year}}</span>
</div>
</a>
@endforeach
</div>

View file

@ -1,33 +0,0 @@
@extends('layout')
@push('head')
<link rel="stylesheet" href="{{ asset('css/seat-chooser.css') }}">
<script src="{{ asset('js/seat-chooser.js') }}" defer></script>
@endpush
<div id="load-screen">
<div id="loading">
<div class="spinner"></div>
<div class="loading-text">Loading...</div>
</div>
</div>
<div class="seat-chooser">
@foreach($seatmatrix as $row)
<div class="row">
@foreach($row as $seat)
@if($seat)
<div class="seat @if($seat->isReserved($showing_id)) reserved @endif"
data-seat-id="{{ $seat->seat_id }}"
data-seat-status="{{ $seat->seat_status }}"
data-seat-row="{{ $seat->seat_row }}"
data-seat-column="{{ $seat->seat_column }}">
<div class="seat-number">{{ $seat->seat_row }}-{{ $seat->seat_column }}</div>
</div>
@else
<div class="no-seat"></div>
@endif
@endforeach
</div>
@endforeach
</div>

View file

@ -7,7 +7,6 @@
@endpush @endpush
<div> <div>
<div id="seat-chooser" data-showing-id="{{ $showing_id }}"> <div id="seat-chooser" data-showing-id="{{ $showing_id }}">
<div id="seat-chooser-status"> <div id="seat-chooser-status">
<h2>Loading...</h2> <h2>Loading...</h2>

View file

@ -16,7 +16,7 @@
<h2>Orders</h2> <h2>Orders</h2>
<hr/> <hr/>
<x-orders :orders="Auth()->user()->orders" /> <x-orders :orders="Auth()->user()->orders"/>
<h2>Permissions</h2> <h2>Permissions</h2>
<hr/> <hr/>

View file

@ -1,7 +1,7 @@
@extends('main.layout') @extends('main.layout')
@push('head') @push('head')
<link rel="stylesheet" href="{{ asset('css/extra.css') }}"> <link rel="stylesheet" href="{{ asset('css/main/jumbotron.css') }}">
<script defer src="{{ asset('js/jumbotron.js') }}"></script> <script defer src="{{ asset('js/jumbotron.js') }}"></script>
@endpush @endpush

View file

@ -2,7 +2,7 @@
@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/main.css') }}"> <link rel="stylesheet" href="{{ asset('css/main/main.css') }}">
@endpush @endpush
@section('body') @section('body')

View file

@ -1,7 +1,7 @@
@extends('main.layout') @extends('main.layout')
@push('head') @push('head')
<link rel="stylesheet" href="{{asset('css/movies.css')}}"> <link rel="stylesheet" href="{{asset('css/main/movies.css')}}">
@endpush @endpush
@section('content') @section('content')
@ -9,17 +9,7 @@
<section> <section>
<h1>Now playing:</h1> <h1>Now playing:</h1>
<hr/> <hr/>
<div id="movies"> <x-movie-cards :movies="$movies"/>
@foreach($movies as $movie)
<a class="movie" href="{{route('movie', ['id' => $movie->movie_id])}}">
<x-cache-image src="{{$movie->movie_image}}" alt="{{$movie->movie_name}} Poster" class="poster" width="200px"/>
<div class="details">
<h2>{{ $movie->movie_name }}</h2>
<span>{{ $movie->movie_description }}</span>
<p>{{ $movie->movie_length }} minutes</p>
</div>
</a>
@endforeach
</section> </section>
@endsection @endsection

View file

@ -30,6 +30,5 @@
<hr/> <hr/>
<div id="summary-total">Total: €0.00</div> <div id="summary-total">Total: €0.00</div>
</div> </div>
<button id="order-button" class="button" disabled=true>Order</button> <button id="order-button" class="button" disabled="true" data-token="{{ Auth::user()->createToken('order-token', ['*'], now()->addMinutes(15))->plainTextToken }}">Order</button>
@endsection @endsection

View file

@ -5,7 +5,7 @@
@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/manage.css') }}"> <link rel="stylesheet" href="{{ asset('css/manage/manage.css') }}">
@endpush @endpush
@section('body') @section('body')

View file

@ -8,17 +8,5 @@
<a href="{{ route('manage.movies.create') }}" class="button" style="height: 2rem; margin: 0.5rem 0;">Add Movie</a> <a href="{{ route('manage.movies.create') }}" class="button" style="height: 2rem; margin: 0.5rem 0;">Add Movie</a>
</div> </div>
<hr/><br/> <hr/><br/>
<div id="movies"> <x-movie-list :movies="$movies"/>
@foreach($movies as $movie)
<a href="/manage/movie/{{$movie->movie_id}}">
<x-cache-image src="{{$movie->movie_image}}" alt="{{$movie->movie_name}} Poster" class="poster" width="200px"/>
<div class="details">
<h3>{{$movie->movie_name}}</h3><br/>
<span>{{ Str::limit($movie->movie_description, 500) }}</span><br/>
<hr/>
<span>{{$movie->movie_length}} min | {{$movie->movie_year}}</span>
</div>
</a>
@endforeach
</div>
@endsection @endsection

View file

@ -0,0 +1,9 @@
@extends('manage.layout')
@push('head')
<link rel="stylesheet" href="{{ asset('css/manage.css') }}">
@endpush
@section('content')
@endsection

View file

@ -112,3 +112,47 @@
$user->save(); $user->save();
return $user->cinemas; return $user->cinemas;
})->name('api.user.assignment'); })->name('api.user.assignment');
// POST /api/order
Route::middleware('auth:sanctum')->post('/order', function (Request $request) {
// [0] showing = showing_id
// [1] seats = [{seat: seat_id, price: price_id}, ...]
// check user
if ($request->user()->exists()) {
return response()->json(['error' => 'user not logged in'], 400);
}
if (!isset($request->showing) || !isset($request->seats)) { // check if user is logged in and showings and seats are set
return response()->json(['error' => 'showing or seats not set'], 400);
}
if ($request->user()->addresses()->count() == 0) { // check if user has an address
return response()->json(['error' => 'no address set'], 400);
}
$order = (new App\Models\Order)->create([
'user_id' => $request->user()->user_id,
'order_number' => substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil(10 / strlen($x)))), 1, 10),
'order_status' => 'pending',
'billing_address_id' => Auth::user()->address()->address_id
]);
$order->save();
// create tickets for each seat
foreach ($request->seats as $seat) {
Log::info('Creating ticket for seat ' . $seat['seat']);
$ticket = (new App\Models\Ticket)->create([
'seat_id' => $seat['seat'],
'price_id' => $seat['price'],
'showing_id' => $request->showing,
'order_id' => $order->order_id,
'user_id' => $request->user()->user_id
]);
$ticket->save();
}
Log::info('Order created: ' . $order->order_number);
return $order;
})->name('api.order');