Add reviews, search autocomplete, SEO meta/schema, user lists
- Review system: ReviewController (store/update/destroy), ReviewPolicy, Review model boot() hooks, review form with Alpine.js (spoiler toggle, edit/delete UX) - Search autocomplete: Alpine.js searchWidget in nav with 300ms debounce, keyboard navigation, 6-result dropdown - SEO: og:image, twitter:card in layout; og sections + Schema.org VideoGame JSON-LD on game show pages - Custom lists: UserList/UserListGame models, ListController, ListPolicy, lists index/show views, Add-to-List button on game pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
93d869cf8c
commit
5c6ba86e15
@@ -0,0 +1,88 @@
|
||||
@extends('layouts.app')
|
||||
@php use Illuminate\Support\Str; @endphp
|
||||
|
||||
@section('title', $list->title . ' — GameLog')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
||||
|
||||
{{-- Header --}}
|
||||
<div class="flex items-start justify-between mb-2">
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<a href="{{ route('users.show', $list->user->username) }}" class="text-sm text-slate-500 hover:text-slate-300 transition-colors">
|
||||
{{ $list->user->username ?? $list->user->name }}
|
||||
</a>
|
||||
@if(!$list->is_public)
|
||||
<span class="text-xs text-slate-500 bg-[#16161d] px-2 py-0.5 rounded border border-white/10">Private</span>
|
||||
@endif
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-white">{{ $list->title }}</h1>
|
||||
</div>
|
||||
@auth
|
||||
@if(auth()->id() === $list->user_id)
|
||||
<form method="POST" action="{{ route('lists.destroy', $list) }}" onsubmit="return confirm('Delete this list and remove all its games?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="text-sm text-red-400 hover:text-red-300 transition-colors">Delete List</button>
|
||||
</form>
|
||||
@endif
|
||||
@endauth
|
||||
</div>
|
||||
|
||||
@if($list->description)
|
||||
<p class="text-slate-400 mb-8 max-w-2xl">{{ $list->description }}</p>
|
||||
@else
|
||||
<div class="mb-8"></div>
|
||||
@endif
|
||||
|
||||
<p class="text-sm text-slate-500 mb-6">{{ $list->listGames->count() }} {{ Str::plural('game', $list->listGames->count()) }}</p>
|
||||
|
||||
@if($list->listGames->isEmpty())
|
||||
<div class="text-center py-16">
|
||||
<p class="text-slate-500">No games in this list yet.</p>
|
||||
@auth
|
||||
@if(auth()->id() === $list->user_id)
|
||||
<p class="text-sm text-slate-600 mt-2">
|
||||
Browse <a href="{{ route('games.index') }}" class="text-purple-400 hover:text-purple-300 transition-colors">games</a>
|
||||
and use "Add to List" to add them here.
|
||||
</p>
|
||||
@endif
|
||||
@endauth
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
||||
@foreach($list->listGames as $entry)
|
||||
<div class="group relative">
|
||||
<x-game-card :game="$entry->game" />
|
||||
@auth
|
||||
@if(auth()->id() === $list->user_id)
|
||||
<button onclick="removeGameFromList({{ $list->id }}, {{ $entry->game->id }}, this)"
|
||||
class="absolute top-2 right-2 w-6 h-6 bg-red-600 hover:bg-red-700 text-white rounded-full text-xs font-bold opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center leading-none">
|
||||
×
|
||||
</button>
|
||||
@endif
|
||||
@endauth
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function removeGameFromList(listId, gameId, btn) {
|
||||
if (!confirm('Remove from list?')) return;
|
||||
fetch(`/lists/${listId}/games/${gameId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
}).then(r => {
|
||||
if (r.ok) btn.closest('.group').remove();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user