progress
This commit is contained in:
@@ -67,7 +67,8 @@ class GameListController extends Controller
|
||||
*/
|
||||
public function edit(GameList $list)
|
||||
{
|
||||
$this->authorize('update', $list);
|
||||
Gate::authorize('update', $list);
|
||||
|
||||
return view('lists.edit', compact('list'));
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ class GameListController extends Controller
|
||||
*/
|
||||
public function update(Request $request, GameList $list)
|
||||
{
|
||||
$this->authorize('update', $list);
|
||||
Gate::authorize('update', $list);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
@@ -96,7 +97,7 @@ class GameListController extends Controller
|
||||
*/
|
||||
public function destroy(GameList $list)
|
||||
{
|
||||
$this->authorize('delete', $list);
|
||||
Gate::authorize('delete', $list);
|
||||
|
||||
if ($list->is_default) {
|
||||
return redirect()->back()->with('error', 'Cannot delete default lists.');
|
||||
@@ -121,7 +122,7 @@ class GameListController extends Controller
|
||||
|
||||
public function removeGame(GameList $list, Game $game)
|
||||
{
|
||||
$this->authorize('update', $list);
|
||||
Gate::authorize('update', $list);
|
||||
|
||||
$list->games()->detach($game->id);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class ReviewController extends Controller
|
||||
$validated = $request->validate([
|
||||
'content' => 'required|string|min:10|max:1000',
|
||||
'recommended' => 'required|boolean',
|
||||
'rating' => 'required|integer|between:1,10',
|
||||
'rating' => 'required|integer|between:1,20',
|
||||
]);
|
||||
|
||||
$review->update($validated);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="none" stroke="currentColor" stroke-width="2" d="M12 2l2.4 7.4h7.6l-6 4.6 2.3 7-6.3-4.6-6.3 4.6 2.3-7-6-4.6h7.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 220 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" stroke="currentColor" stroke-width="2" d="M12 2l2.4 7.4h7.6l-6 4.6 2.3 7-6.3-4.6-6.3 4.6 2.3-7-6-4.6h7.6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
@@ -3,17 +3,56 @@
|
||||
@section('title', $game->name)
|
||||
|
||||
@section('content')
|
||||
<div class="container py-4">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<img src="{{ $game->cover_url }}" class="card-img-top" alt="{{ $game->name }}">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="card-title mb-0">{{ $game->name }}</h5>
|
||||
<p class="text-muted mb-0">{{ $game->first_release_date->format('Y') }}</p>
|
||||
<!-- Hero Section -->
|
||||
<div class="game-hero position-relative mb-5">
|
||||
@if($game->screenshots()->count())
|
||||
<div class="game-hero-bg position-absolute w-100 h-100" style="background: linear-gradient(rgba(18, 18, 18, 0.7), rgba(18, 18, 18, 1)), url('{{ $game->screenshots()->first()->url }}') no-repeat center center; background-size: cover; filter: blur(3px); z-index: -1;"></div>
|
||||
@else
|
||||
<div class="game-hero-bg position-absolute w-100 h-100" style="background: linear-gradient(rgba(18, 18, 18, 0.9), rgba(18, 18, 18, 1)); z-index: -1;"></div>
|
||||
@endif
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-3 col-md-4 mb-4 mb-md-0">
|
||||
<div class="game-cover position-relative">
|
||||
<img src="{{ $game->cover_url }}" class="img-fluid rounded shadow-lg" alt="{{ $game->name }}">
|
||||
@if($game->rating)
|
||||
<div class="position-absolute top-0 end-0 bg-dark bg-opacity-75 p-2 rounded-circle m-2 shadow">
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<span class="fw-bold text-warning">{{ number_format($game->rating, 1) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 col-md-8">
|
||||
<h1 class="display-4 fw-bold mb-2">{{ $game->name }}</h1>
|
||||
<div class="d-flex flex-wrap gap-2 mb-3">
|
||||
@foreach($game->genres()->get() as $genre)
|
||||
<span class="badge bg-primary">{{ $genre->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mb-4">
|
||||
@foreach($game->platforms()->get() as $platform)
|
||||
<span class="badge bg-secondary">{{ $platform->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
<p class="lead mb-4">{{ Str::limit($game->summary, 200) }}</p>
|
||||
<div class="d-flex flex-wrap gap-3 align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="far fa-calendar-alt me-2"></i>
|
||||
<span>{{ $game->first_release_date->format('F j, Y') }}</span>
|
||||
</div>
|
||||
@if($game->rating)
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rating-stars d-flex gap-1">
|
||||
@for($i = 1; $i <= 5; $i++)
|
||||
<i class="fas fa-star {{ $i <= ($game->rating/20) ? 'text-warning' : 'text-secondary' }}"></i>
|
||||
@endfor
|
||||
</div>
|
||||
<span class="ms-2">({{ number_format($game->rating_count) }} ratings)</span>
|
||||
</div>
|
||||
@endif
|
||||
@auth
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="addToListDropdown"
|
||||
@@ -41,112 +80,212 @@
|
||||
</ul>
|
||||
</div>
|
||||
@endauth
|
||||
@auth
|
||||
<a href="{{ route('games.reviews.create', $game) }}" class="btn btn-gradient">
|
||||
<i class="fas fa-star me-2"></i>Write a Review
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- Tabs Navigation -->
|
||||
<ul class="nav nav-tabs mb-4" id="gameDetailsTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="about-tab" data-bs-toggle="tab" data-bs-target="#about" type="button" role="tab" aria-controls="about" aria-selected="true">
|
||||
<i class="fas fa-info-circle me-2"></i>About
|
||||
</button>
|
||||
</li>
|
||||
@if($game->screenshots()->count())
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="media-tab" data-bs-toggle="tab" data-bs-target="#media" type="button" role="tab" aria-controls="media" aria-selected="false">
|
||||
<i class="fas fa-images me-2"></i>Media
|
||||
</button>
|
||||
</li>
|
||||
@endif
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="reviews-tab" data-bs-toggle="tab" data-bs-target="#reviews" type="button" role="tab" aria-controls="reviews" aria-selected="false">
|
||||
<i class="fas fa-comments me-2"></i>Reviews
|
||||
@if($game->reviews->count() > 0)
|
||||
<span class="badge bg-primary ms-1">{{ $game->reviews->count() }}</span>
|
||||
@endif
|
||||
</button>
|
||||
</li>
|
||||
@if($game->similar_games->count() > 0)
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="similar-tab" data-bs-toggle="tab" data-bs-target="#similar" type="button" role="tab" aria-controls="similar" aria-selected="false">
|
||||
<i class="fas fa-gamepad me-2"></i>Similar Games
|
||||
</button>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
|
||||
<!-- Tabs Content -->
|
||||
<div class="tab-content" id="gameDetailsTabsContent">
|
||||
<!-- About Tab -->
|
||||
<div class="tab-pane fade show active" id="about" role="tabpanel" aria-labelledby="about-tab">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Released on</h5>
|
||||
<h5 class="mb-0">Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
@foreach($game->platforms()->get() as $platform)
|
||||
<span class="badge bg-secondary">{{ $platform->name }}</span>
|
||||
<p class="card-text">{{ $game->summary }}</p>
|
||||
@if($game->storyline)
|
||||
<h6 class="mt-4 mb-2">Storyline</h6>
|
||||
<p class="card-text">{{ $game->storyline }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($game->dlcs->count() > 0)
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">DLCs & Expansions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
@foreach($game->dlcs as $dlc)
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card bg-dark h-100">
|
||||
<img src="{{ $dlc->cover_url }}" class="card-img-top" alt="{{ $dlc->name }}">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">{{ $dlc->name }}</h6>
|
||||
<a href="{{ route('games.show', $dlc) }}" class="btn btn-sm btn-outline-primary mt-2">View Details</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Genres</h5>
|
||||
<h5 class="mb-0">Game Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<ul class="list-group list-group-flush bg-transparent">
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span><i class="far fa-calendar-alt me-2"></i>Release Date</span>
|
||||
<span class="text-end">{{ $game->first_release_date->format('F j, Y') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
@if($game->rating)
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span><i class="fas fa-star me-2"></i>Rating</span>
|
||||
<span class="text-end">{{ number_format($game->rating, 1) }}/100 ({{ number_format($game->rating_count) }} ratings)</span>
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div>
|
||||
<span><i class="fas fa-tags me-2"></i>Genres</span>
|
||||
<div class="d-flex flex-wrap gap-1 mt-2">
|
||||
@foreach($game->genres()->get() as $genre)
|
||||
<span class="badge bg-primary">{{ $genre->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div>
|
||||
<span><i class="fas fa-desktop me-2"></i>Platforms</span>
|
||||
<div class="d-flex flex-wrap gap-1 mt-2">
|
||||
@foreach($game->platforms()->get() as $platform)
|
||||
<span class="badge bg-secondary">{{ $platform->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($game->rating)
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted">Rating</h6>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rating-stars d-flex gap-1">
|
||||
@for($i = 1; $i <= 10; $i++)
|
||||
<i class="fas fa-star {{ $i <= $game->rating ? 'text-warning' : 'text-secondary' }}"></i>
|
||||
@endfor
|
||||
</div>
|
||||
<span class="ms-2">({{ $game->rating_count }} ratings)</span>
|
||||
</div>
|
||||
</li>
|
||||
@if($game->url)
|
||||
<li class="list-group-item bg-dark text-white border-secondary">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span><i class="fas fa-external-link-alt me-2"></i>Website</span>
|
||||
<a href="{{ $game->url }}" target="_blank" class="text-primary">Visit</a>
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">About</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text">{{ $game->summary }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Media Tab -->
|
||||
@if($game->screenshots()->count() || $game->videos()->count())
|
||||
<div class="tab-pane fade" id="media" role="tabpanel" aria-labelledby="media-tab">
|
||||
@if($game->screenshots()->count())
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Screenshots</h5>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#screenshotsCollapse">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="collapse show" id="screenshotsCollapse">
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
@foreach($game->screenshots()->get() as $screenshot)
|
||||
<div class="col-md-6">
|
||||
<img src="{{ $screenshot->url }}" class="img-fluid rounded" alt="Game Screenshot">
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<a href="{{ $screenshot->url }}" data-lightbox="game-screenshots" data-title="{{ $game->name }} Screenshot">
|
||||
<img src="{{ $screenshot->url }}" class="img-fluid rounded shadow-sm hover-scale" alt="Game Screenshot">
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($game->videos()->count())
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Videos</h5>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#videosCollapse">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="collapse show" id="videosCollapse">
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
@foreach($game->videos()->get() as $video)
|
||||
<div class="col-12">
|
||||
<div class="col-md-6">
|
||||
<div class="card bg-dark border-secondary">
|
||||
<div class="ratio ratio-16x9">
|
||||
<iframe src="https://www.youtube.com/embed/{{ $video->video_id }}"
|
||||
title="{{ $video->name }}" allowfullscreen></iframe>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">{{ $video->name }}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card bg-dark text-white">
|
||||
<!-- Reviews Tab -->
|
||||
<div class="tab-pane fade" id="reviews" role="tabpanel" aria-labelledby="reviews-tab">
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Reviews</h5>
|
||||
<h5 class="mb-0">User Reviews</h5>
|
||||
@auth
|
||||
<a href="{{ route('games.reviews.create', $game) }}" class="btn btn-gradient btn-sm">
|
||||
<i class="fas fa-plus me-2"></i>Write a Review
|
||||
@@ -155,15 +294,76 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($game->reviews->isEmpty())
|
||||
<p class="text-muted mb-0">No reviews yet. Be the first to review this game!</p>
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-comments fa-3x text-muted mb-3"></i>
|
||||
<p class="text-muted mb-3">No reviews yet. Be the first to review this game!</p>
|
||||
@auth
|
||||
<a href="{{ route('games.reviews.create', $game) }}" class="btn btn-gradient">
|
||||
<i class="fas fa-plus me-2"></i>Write a Review
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="btn btn-gradient">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>Login to Write a Review
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
@else
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card bg-dark border-secondary h-100">
|
||||
<div class="card-body text-center">
|
||||
<h2 class="display-4 fw-bold text-warning mb-0">
|
||||
{{ number_format($game->reviews->avg('rating'), 1) }}
|
||||
</h2>
|
||||
<div class="rating-stars d-flex gap-1 justify-content-center my-2">
|
||||
@for($i = 1; $i <= 5; $i++)
|
||||
<i class="fas fa-star {{ $i <= ($game->reviews->avg('rating')/2) ? 'text-warning' : 'text-secondary' }}"></i>
|
||||
@endfor
|
||||
</div>
|
||||
<p class="text-muted">Based on {{ $game->reviews->count() }} reviews</p>
|
||||
<div class="d-flex justify-content-center gap-3">
|
||||
<div class="text-center">
|
||||
<span class="d-block fw-bold text-success">{{ $game->reviews->where('recommended', true)->count() }}</span>
|
||||
<small class="text-muted">Recommended</small>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<span class="d-block fw-bold text-danger">{{ $game->reviews->where('recommended', false)->count() }}</span>
|
||||
<small class="text-muted">Not Recommended</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card bg-dark border-secondary h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="mb-3">Rating Distribution</h6>
|
||||
@for($i = 10; $i >= 1; $i--)
|
||||
@php
|
||||
$count = $game->reviews->where('rating', '>=', ($i-1)*2)->where('rating', '<', $i*2)->count();
|
||||
$percentage = $game->reviews->count() > 0 ? ($count / $game->reviews->count()) * 100 : 0;
|
||||
@endphp
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="rating-label me-2" style="width: 30px;">{{ $i*2-1 }}-{{ $i*2 }}</div>
|
||||
<div class="progress flex-grow-1" style="height: 10px;">
|
||||
<div class="progress-bar bg-warning" role="progressbar" style="width: {{ $percentage }}%"></div>
|
||||
</div>
|
||||
<div class="ms-2" style="width: 40px;">{{ $count }}</div>
|
||||
</div>
|
||||
@endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="reviews-list">
|
||||
@foreach($game->reviews as $review)
|
||||
<div class="card bg-dark text-white mb-4">
|
||||
<div class="card bg-dark border-secondary mb-4">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<i class="fas fa-user-circle fa-2x"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0">
|
||||
@@ -177,10 +377,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rating-stars d-flex gap-1 me-2">
|
||||
@for($i = 1; $i <= 10; $i++)
|
||||
<i class="fas fa-star {{ $i <= $review->rating ? 'text-warning' : 'text-secondary' }}"></i>
|
||||
@endfor
|
||||
<div class="rating-badge me-2 px-2 py-1 rounded" style="background-color: {{ $review->rating >= 15 ? '#28a745' : ($review->rating >= 10 ? '#ffc107' : '#dc3545') }};">
|
||||
<span class="fw-bold">{{ $review->rating }}/20</span>
|
||||
</div>
|
||||
@if($review->recommended)
|
||||
<span class="badge bg-success">Recommended</span>
|
||||
@@ -192,7 +390,10 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="review-content">
|
||||
{!! nl2br(e($review->content)) !!}
|
||||
{!! nl2br(e(Str::limit($review->content, 300))) !!}
|
||||
@if(strlen($review->content) > 300)
|
||||
<a href="{{ route('reviews.show', $review) }}" class="text-primary">Read more</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-dark border-top border-secondary">
|
||||
@@ -218,10 +419,109 @@
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Similar Games Tab -->
|
||||
@if($game->similar_games->count() > 0)
|
||||
<div class="tab-pane fade" id="similar" role="tabpanel" aria-labelledby="similar-tab">
|
||||
<div class="card bg-dark text-white">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Similar Games You Might Like</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-4">
|
||||
@foreach($game->similar_games as $similar)
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card bg-dark h-100 hover-scale">
|
||||
<a href="{{ route('games.show', $similar) }}" class="text-decoration-none">
|
||||
<img src="{{ $similar->cover_url }}" class="card-img-top" alt="{{ $similar->name }}">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title text-white">{{ $similar->name }}</h6>
|
||||
<p class="card-text text-muted small">{{ $similar->first_release_date->format('Y') }}</p>
|
||||
@if($similar->rating)
|
||||
<div class="d-flex align-items-center mt-2">
|
||||
<div class="rating-stars d-flex gap-1">
|
||||
@for($i = 1; $i <= 5; $i++)
|
||||
<i class="fas fa-star {{ $i <= ($similar->rating/20) ? 'text-warning' : 'text-secondary' }}"></i>
|
||||
@endfor
|
||||
</div>
|
||||
<span class="ms-2 small">{{ number_format($similar->rating, 1) }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.game-hero {
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hover-scale {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.hover-scale:hover {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid #343a40;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
color: #eaeaea;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link:hover {
|
||||
border-color: transparent;
|
||||
border-bottom: 2px solid var(--accent);
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: var(--accent);
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
border-bottom: 2px solid var(--accent);
|
||||
}
|
||||
|
||||
.btn-gradient {
|
||||
background: linear-gradient(90deg, var(--primary), var(--accent));
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-gradient:hover {
|
||||
opacity: 0.9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
linear-gradient(90deg, var(--primary), var(--accent)) border-box;
|
||||
}
|
||||
</style>
|
||||
@stack('styles')
|
||||
</head>
|
||||
<body class="font-sans antialiased">
|
||||
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
|
||||
@@ -106,5 +107,6 @@
|
||||
|
||||
<!-- Bootstrap Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,37 +1,21 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', isset($review) ? 'Edit Review' : 'Write a Review')
|
||||
|
||||
@section('content')
|
||||
<div class="container py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card bg-dark text-white">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">{{ isset($review) ? 'Edit Review' : 'Write a Review' }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ isset($review) ? route('reviews.update', $review) : route('games.reviews.store', $game) }}" method="POST">
|
||||
@csrf
|
||||
@if(isset($review))
|
||||
@method('PUT')
|
||||
@endif
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="rating" class="form-label">Rating</label>
|
||||
<select name="rating" id="rating" class="star-rating" required>
|
||||
<option value="">Select a rating</option>
|
||||
<option value="10" {{ isset($review) && $review->rating == 10 ? 'selected' : '' }}>10 - Masterpiece</option>
|
||||
<option value="9" {{ isset($review) && $review->rating == 9 ? 'selected' : '' }}>9 - Amazing</option>
|
||||
<option value="8" {{ isset($review) && $review->rating == 8 ? 'selected' : '' }}>8 - Great</option>
|
||||
<option value="7" {{ isset($review) && $review->rating == 7 ? 'selected' : '' }}>7 - Good</option>
|
||||
<option value="6" {{ isset($review) && $review->rating == 6 ? 'selected' : '' }}>6 - Fair</option>
|
||||
<option value="5" {{ isset($review) && $review->rating == 5 ? 'selected' : '' }}>5 - Average</option>
|
||||
<option value="4" {{ isset($review) && $review->rating == 4 ? 'selected' : '' }}>4 - Below Average</option>
|
||||
<option value="3" {{ isset($review) && $review->rating == 3 ? 'selected' : '' }}>3 - Poor</option>
|
||||
<option value="2" {{ isset($review) && $review->rating == 2 ? 'selected' : '' }}>2 - Bad</option>
|
||||
<option value="1" {{ isset($review) && $review->rating == 1 ? 'selected' : '' }}>1 - Terrible</option>
|
||||
</select>
|
||||
<label for="rating" class="form-label">Rating (1-20)</label>
|
||||
<div class="interactive-stars mb-2">
|
||||
<div class="stars-container" id="starsContainer">
|
||||
@for ($i = 1; $i <= 10; $i++)
|
||||
<i class="fas fa-star star-icon" data-value="{{ $i }}"></i>
|
||||
@endfor
|
||||
</div>
|
||||
<div class="rating-value-display mt-2">
|
||||
<span id="ratingValue">{{ isset($review) ? $review->rating : 0 }}</span>/10
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="rating" id="ratingInput" value="{{ isset($review) ? $review->rating : '' }}" required>
|
||||
@error('rating')
|
||||
<div class="text-danger mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
@@ -57,45 +41,89 @@
|
||||
{{ isset($review) ? 'Update Review' : 'Submit Review' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('styles')
|
||||
<link href="{{ asset('css/star-rating.css') }}" rel="stylesheet">
|
||||
<style>
|
||||
.star-rating {
|
||||
--gl-star-color: #fdd835;
|
||||
--gl-star-color-inactive: #dcdce6;
|
||||
--gl-star-size: 32px;
|
||||
--gl-tooltip-border-radius: 4px;
|
||||
--gl-tooltip-font-size: 1rem;
|
||||
--gl-tooltip-font-weight: 400;
|
||||
--gl-tooltip-line-height: 1;
|
||||
--gl-tooltip-margin: 12px;
|
||||
--gl-tooltip-padding: .5em 1em;
|
||||
--gl-tooltip-background: rgba(17,17,17, .9);
|
||||
--gl-tooltip-color: #fff;
|
||||
.interactive-stars {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stars-container {
|
||||
display: inline-flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.star-icon {
|
||||
font-size: 32px;
|
||||
color: #dcdce6;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.star-icon.active {
|
||||
color: #fdd835;
|
||||
}
|
||||
|
||||
.star-icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.rating-value-display {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #fdd835;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script src="{{ asset('js/star-rating.js') }}"></script>
|
||||
<script>
|
||||
var stars = new StarRating('.star-rating', {
|
||||
classNames: {
|
||||
active: "gl-active",
|
||||
base: "gl-star-rating",
|
||||
selected: "gl-selected",
|
||||
},
|
||||
clearable: true,
|
||||
maxStars: 10,
|
||||
tooltip: 'Select a Rating'
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const stars = document.querySelectorAll('.star-icon');
|
||||
const ratingInput = document.getElementById('ratingInput');
|
||||
const ratingValue = document.getElementById('ratingValue');
|
||||
|
||||
// Initialize stars based on existing rating
|
||||
const initialRating = parseInt(ratingInput.value) || 0;
|
||||
updateStars(initialRating);
|
||||
|
||||
stars.forEach(star => {
|
||||
// Hover effect
|
||||
star.addEventListener('mouseenter', function() {
|
||||
const value = parseInt(this.dataset.value);
|
||||
highlightStars(value);
|
||||
});
|
||||
|
||||
// Click to set rating
|
||||
star.addEventListener('click', function() {
|
||||
const value = parseInt(this.dataset.value);
|
||||
ratingInput.value = value;
|
||||
ratingValue.textContent = value;
|
||||
updateStars(value);
|
||||
});
|
||||
});
|
||||
|
||||
// Reset to selected rating when mouse leaves the container
|
||||
document.getElementById('starsContainer').addEventListener('mouseleave', function() {
|
||||
const selectedRating = parseInt(ratingInput.value) || 0;
|
||||
updateStars(selectedRating);
|
||||
});
|
||||
|
||||
function highlightStars(value) {
|
||||
stars.forEach(s => {
|
||||
const starValue = parseInt(s.dataset.value);
|
||||
s.classList.toggle('active', starValue <= value);
|
||||
});
|
||||
ratingValue.textContent = value;
|
||||
}
|
||||
|
||||
function updateStars(value) {
|
||||
stars.forEach(s => {
|
||||
const starValue = parseInt(s.dataset.value);
|
||||
s.classList.toggle('active', starValue <= value);
|
||||
});
|
||||
ratingValue.textContent = value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user