lots of progress

This commit is contained in:
2025-04-12 17:18:33 +03:00
parent 8d230e8e67
commit 3d4260d57e
44 changed files with 2590 additions and 2883 deletions
+167 -3
View File
@@ -1,3 +1,167 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
:root {
--primary: #7F00FF;
--accent: #00C9FF;
--background: #121212;
--text: #EAEAEA;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--background);
color: var(--text);
}
.gradient-text {
background: linear-gradient(90deg, var(--primary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.gradient-border {
border: 2px solid transparent;
background: linear-gradient(var(--background), var(--background)) padding-box,
linear-gradient(90deg, var(--primary), var(--accent)) border-box;
}
.btn-gradient {
background: linear-gradient(90deg, var(--primary), var(--accent));
border: none;
color: white;
}
.btn-gradient:hover {
opacity: 0.9;
color: white;
}
.btn-outline-gradient {
border: 2px solid transparent;
background: linear-gradient(var(--background), var(--background)) padding-box,
linear-gradient(90deg, var(--primary), var(--accent)) border-box;
color: white;
}
.btn-outline-gradient:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
/* Form Styles */
.form-control {
background-color: var(--background) !important;
border-color: rgba(255, 255, 255, 0.1) !important;
color: var(--text) !important;
}
.form-control:focus {
background-color: var(--background) !important;
border-color: var(--primary) !important;
box-shadow: 0 0 0 0.25rem rgba(127, 0, 255, 0.25) !important;
color: var(--text) !important;
}
.form-check-input {
background-color: var(--background) !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.form-check-input:checked {
background-color: var(--primary) !important;
border-color: var(--primary) !important;
}
.form-check-input:focus {
border-color: var(--primary) !important;
box-shadow: 0 0 0 0.25rem rgba(127, 0, 255, 0.25) !important;
}
/* Alert Styles */
.alert {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text);
}
.alert-success {
background-color: rgba(40, 167, 69, 0.1);
border-color: rgba(40, 167, 69, 0.2);
color: #28a745;
}
.alert-danger {
background-color: rgba(220, 53, 69, 0.1);
border-color: rgba(220, 53, 69, 0.2);
color: #dc3545;
}
/* Custom Pagination Styles */
.pagination {
--bs-pagination-color: var(--text);
--bs-pagination-bg: var(--background);
--bs-pagination-border-color: transparent;
--bs-pagination-hover-color: var(--text);
--bs-pagination-hover-bg: rgba(255, 255, 255, 0.1);
--bs-pagination-hover-border-color: transparent;
--bs-pagination-focus-color: var(--text);
--bs-pagination-focus-bg: rgba(255, 255, 255, 0.1);
--bs-pagination-focus-box-shadow: none;
--bs-pagination-active-color: white;
--bs-pagination-active-bg: linear-gradient(90deg, var(--primary), var(--accent));
--bs-pagination-active-border-color: transparent;
--bs-pagination-disabled-color: #6c757d;
--bs-pagination-disabled-bg: var(--background);
--bs-pagination-disabled-border-color: transparent;
}
.page-link {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
margin: 0 0.25rem;
transition: all 0.2s ease-in-out;
}
.page-link:hover {
transform: translateY(-1px);
}
.page-item.active .page-link {
background: linear-gradient(90deg, var(--primary), var(--accent));
border: none;
}
.page-item.disabled .page-link {
background-color: var(--background);
border-color: transparent;
}
/* Custom dropdown styles */
.dropdown-menu {
background-color: var(--background);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.dropdown-item {
color: var(--text);
}
.dropdown-item:hover {
background-color: rgba(255, 255, 255, 0.1);
color: var(--text);
}
/* Custom badge styles */
.badge {
padding: 0.5em 0.75em;
font-weight: 500;
}
.badge.bg-primary {
background: linear-gradient(90deg, var(--primary), var(--accent)) !important;
}
.badge.bg-secondary {
background: rgba(255, 255, 255, 0.1) !important;
color: var(--text);
}
+2 -5
View File
@@ -1,7 +1,4 @@
import 'bootstrap';
import './bootstrap';
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
// Custom JavaScript can be added here
+43 -31
View File
@@ -1,47 +1,59 @@
<x-guest-layout>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
@extends('layouts.auth')
@section('title', 'Login')
@section('subtitle', 'Welcome back! Please enter your details.')
@section('content')
<form method="POST" action="{{ route('login') }}">
@csrf
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
<div class="mb-3">
<label for="email" class="form-label text-gray-400">Email</label>
<input id="email" type="email" class="form-control bg-dark text-white border-gray-800 @error('email') is-invalid @enderror"
name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="current-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
<div class="mb-3">
<label for="password" class="form-label text-gray-400">Password</label>
<input id="password" type="password" class="form-control bg-dark text-white border-gray-800 @error('password') is-invalid @enderror"
name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Remember Me -->
<div class="block mt-4">
<label for="remember_me" class="inline-flex items-center">
<input id="remember_me" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remember">
<span class="ms-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
<div class="mb-3 form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label text-gray-400" for="remember">
Remember me
</label>
</div>
<div class="flex items-center justify-end mt-4">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-gradient">
Login
</button>
</div>
<div class="text-center mt-3">
@if (Route::has('password.request'))
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('password.request') }}">
{{ __('Forgot your password?') }}
<a class="text-decoration-none text-gray-400" href="{{ route('password.request') }}">
Forgot your password?
</a>
@endif
<x-primary-button class="ms-3">
{{ __('Log in') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
<div class="text-center mt-4">
<p class="text-gray-400 mb-0">Don't have an account?
<a href="{{ route('register') }}" class="text-decoration-none gradient-text">Sign up</a>
</p>
</div>
@endsection
+48 -39
View File
@@ -1,52 +1,61 @@
<x-guest-layout>
@extends('layouts.auth')
@section('title', 'Register')
@section('subtitle', 'Create your account to start tracking your games.')
@section('content')
<form method="POST" action="{{ route('register') }}">
@csrf
<!-- Name -->
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
<x-input-error :messages="$errors->get('name')" class="mt-2" />
<div class="mb-3">
<label for="name" class="form-label text-gray-400">Name</label>
<input id="name" type="text" class="form-control bg-dark text-white border-gray-800 @error('name') is-invalid @enderror"
name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
<div class="mb-3">
<label for="email" class="form-label text-gray-400">Email</label>
<input id="email" type="email" class="form-control bg-dark text-white border-gray-800 @error('email') is-invalid @enderror"
name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
<div class="mb-3">
<label for="password" class="form-label text-gray-400">Password</label>
<input id="password" type="password" class="form-control bg-dark text-white border-gray-800 @error('password') is-invalid @enderror"
name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
<div class="mb-3">
<label for="password-confirm" class="form-label text-gray-400">Confirm Password</label>
<input id="password-confirm" type="password" class="form-control bg-dark text-white border-gray-800"
name="password_confirmation" required autocomplete="new-password">
</div>
<div class="flex items-center justify-end mt-4">
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('login') }}">
{{ __('Already registered?') }}
</a>
<x-primary-button class="ms-4">
{{ __('Register') }}
</x-primary-button>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-gradient">
Register
</button>
</div>
</form>
</x-guest-layout>
<div class="text-center mt-4">
<p class="text-gray-400 mb-0">Already have an account?
<a href="{{ route('login') }}" class="text-decoration-none gradient-text">Login</a>
</p>
</div>
@endsection
+161
View File
@@ -0,0 +1,161 @@
@extends('layouts.app')
@section('title', 'Games - ClearGame')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2 fw-bold gradient-text">All Games</h1>
<div class="d-flex gap-2">
<form action="{{ route('games.index') }}" method="GET" class="d-flex me-2">
<input type="text"
name="search"
class="form-control bg-dark text-white border-0"
placeholder="Search games..."
value="{{ request('search') }}"
style="min-width: 250px;">
<button type="submit" class="btn btn-gradient ms-2">
<i class="fas fa-search"></i>
</button>
</form>
<div class="dropdown">
<button class="btn btn-outline-gradient dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-sort me-1"></i> Sort by
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ route('games.index', array_merge(request()->query(), ['sort' => 'first_release_date', 'direction' => 'desc'])) }}">
<i class="fas fa-calendar me-2"></i> Release Date
</a></li>
<li><a class="dropdown-item" href="{{ route('games.index', array_merge(request()->query(), ['sort' => 'rating', 'direction' => 'desc'])) }}">
<i class="fas fa-star me-2"></i> Rating
</a></li>
<li><a class="dropdown-item" href="{{ route('games.index', array_merge(request()->query(), ['sort' => 'rating_count', 'direction' => 'desc'])) }}">
<i class="fas fa-fire me-2"></i> Popularity
</a></li>
</ul>
</div>
<div class="dropdown">
<button class="btn btn-outline-gradient dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-filter me-1"></i> Filter
</button>
<ul class="dropdown-menu p-3" style="min-width: 300px;">
<li class="mb-2">
<label class="form-label text-white mb-1"><i class="fas fa-tv me-1"></i> Platform</label>
<select name="platform" class="form-select bg-dark text-white border-0" onchange="this.form.submit()">
<option value="">All Platforms</option>
@foreach($platforms as $platform)
<option value="{{ $platform->slug }}" {{ request('platform') == $platform->slug ? 'selected' : '' }}>
{{ $platform->name }}
</option>
@endforeach
</select>
</li>
<li class="mb-2">
<label class="form-label text-white mb-1"><i class="fas fa-tags me-1"></i> Genre</label>
<select name="genre" class="form-select bg-dark text-white border-0" onchange="this.form.submit()">
<option value="">All Genres</option>
@foreach($genres as $genre)
<option value="{{ $genre->slug }}" {{ request('genre') == $genre->slug ? 'selected' : '' }}>
{{ $genre->name }}
</option>
@endforeach
</select>
</li>
<li class="mb-2">
<label class="form-label text-white mb-1"><i class="fas fa-calendar-alt me-1"></i> Release Year</label>
<select name="year" class="form-select bg-dark text-white border-0" onchange="this.form.submit()">
<option value="">All Years</option>
@foreach($years as $year)
<option value="{{ $year }}" {{ request('year') == $year ? 'selected' : '' }}>
{{ $year }}
</option>
@endforeach
</select>
</li>
<li class="mb-2">
<label class="form-label text-white mb-1"><i class="fas fa-star me-1"></i> Rating Range</label>
<select name="rating_range" class="form-select bg-dark text-white border-0" onchange="this.form.submit()">
<option value="">All Ratings</option>
<option value="90-100" {{ request('rating_range') == '90-100' ? 'selected' : '' }}>90+</option>
<option value="80-89" {{ request('rating_range') == '80-89' ? 'selected' : '' }}>80-89</option>
<option value="70-79" {{ request('rating_range') == '70-79' ? 'selected' : '' }}>70-79</option>
<option value="60-69" {{ request('rating_range') == '60-69' ? 'selected' : '' }}>60-69</option>
<option value="0-59" {{ request('rating_range') == '0-59' ? 'selected' : '' }}>Below 60</option>
</select>
</li>
<li class="mt-3">
<a href="{{ route('games.index') }}" class="btn btn-outline-gradient btn-sm w-100">
<i class="fas fa-times me-1"></i> Clear Filters
</a>
</li>
</ul>
</div>
</div>
</div>
@if(request()->hasAny(['platform', 'genre', 'year', 'rating_range']))
<div class="mb-4">
<div class="d-flex flex-wrap gap-2">
@if(request('platform'))
<span class="badge bg-primary">
<i class="fas fa-tv me-1"></i> {{ $platforms->firstWhere('slug', request('platform'))->name }}
<a href="{{ route('games.index', array_merge(request()->except('platform'), ['page' => 1])) }}" class="text-white ms-2">×</a>
</span>
@endif
@if(request('genre'))
<span class="badge bg-primary">
<i class="fas fa-tags me-1"></i> {{ $genres->firstWhere('slug', request('genre'))->name }}
<a href="{{ route('games.index', array_merge(request()->except('genre'), ['page' => 1])) }}" class="text-white ms-2">×</a>
</span>
@endif
@if(request('year'))
<span class="badge bg-primary">
<i class="fas fa-calendar-alt me-1"></i> {{ request('year') }}
<a href="{{ route('games.index', array_merge(request()->except('year'), ['page' => 1])) }}" class="text-white ms-2">×</a>
</span>
@endif
@if(request('rating_range'))
<span class="badge bg-primary">
<i class="fas fa-star me-1"></i> {{ request('rating_range') }}
<a href="{{ route('games.index', array_merge(request()->except('rating_range'), ['page' => 1])) }}" class="text-white ms-2">×</a>
</span>
@endif
</div>
</div>
@endif
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-4">
@foreach($games as $game)
<div class="col">
<div class="card gradient-border h-100">
<img src="{{ $game->cover_url ?? 'https://via.placeholder.com/300x400' }}"
class="card-img-top"
alt="{{ $game->name }}"
style="height: 300px; object-fit: cover;">
<div class="card-body">
<h5 class="card-title">{{ $game->name }}</h5>
<p class="card-text text-gray-400 small">
{{ Str::limit($game->summary, 100) }}
</p>
<div class="d-flex gap-2 mb-3">
@if($game->rating)
<span class="badge bg-primary"><i class="fas fa-star me-1"></i> {{ number_format($game->rating, 1) }}</span>
@endif
@if($game->first_release_date)
<span class="badge bg-secondary">
<i class="fas fa-calendar-alt me-1"></i> {{ \Carbon\Carbon::parse($game->first_release_date)->format('Y') }}
</span>
@endif
</div>
<a href="{{ route('games.show', $game->slug) }}" class="btn btn-gradient w-100">
<i class="fas fa-info-circle me-1"></i> View Details
</a>
</div>
</div>
</div>
@endforeach
</div>
<div class="d-flex justify-content-center mt-5">
{{ $games->links() }}
</div>
@endsection
+177
View File
@@ -0,0 +1,177 @@
@extends('layouts.app')
@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>
</div>
@auth
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="addToListDropdown"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-plus me-2"></i>Add to List
</button>
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="addToListDropdown">
@foreach(Auth::user()->lists as $list)
<li>
<form action="{{ route('lists.addGame', $list) }}" method="POST" class="d-inline">
@csrf
<input type="hidden" name="game_id" value="{{ $game->id }}">
<button type="submit" class="dropdown-item">
<i class="fas fa-list me-2"></i>{{ $list->name }}
</button>
</form>
</li>
@endforeach
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="{{ route('lists.create') }}">
<i class="fas fa-plus me-2"></i>Create New List
</a>
</li>
</ul>
</div>
@endauth
</div>
</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">
<div class="mb-3">
<h6 class="text-muted">Platforms</h6>
<div class="d-flex flex-wrap gap-2">
@foreach($game->platforms()->get() as $platform)
<span class="badge bg-secondary">{{ $platform->name }}</span>
@endforeach
</div>
</div>
<div class="mb-3">
<h6 class="text-muted">Genres</h6>
<div class="d-flex flex-wrap gap-2">
@foreach($game->genres()->get() as $genre)
<span class="badge bg-primary">{{ $genre->name }}</span>
@endforeach
</div>
</div>
@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>
</div>
@endif
</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>
@if($game->screenshots()->count())
<div class="card bg-dark text-white mb-4">
<div class="card-header">
<h5 class="mb-0">Screenshots</h5>
</div>
<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>
@endforeach
</div>
</div>
</div>
@endif
@if($game->videos()->count())
<div class="card bg-dark text-white mb-4">
<div class="card-header">
<h5 class="mb-0">Videos</h5>
</div>
<div class="card-body">
<div class="row g-3">
@foreach($game->videos()->get() as $video)
<div class="col-12">
<div class="ratio ratio-16x9">
<iframe src="https://www.youtube.com/embed/{{ $video->video_id }}"
title="{{ $video->name }}" allowfullscreen></iframe>
</div>
</div>
@endforeach
</div>
</div>
</div>
@endif
<div class="card bg-dark text-white">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">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
</a>
@endauth
</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>
@else
@foreach($game->reviews as $review)
<div class="mb-4 pb-4 border-bottom border-secondary">
<div class="d-flex justify-content-between align-items-center mb-2">
<div>
<h6 class="mb-0">{{ $review->user->name }}</h6>
<small class="text-muted">{{ $review->created_at->diffForHumans() }}</small>
</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>
@if($review->recommended)
<span class="badge bg-success">Recommended</span>
@else
<span class="badge bg-danger">Not Recommended</span>
@endif
</div>
</div>
<p class="mb-0">{{ $review->content }}</p>
</div>
@endforeach
@endif
</div>
</div>
</div>
</div>
</div>
@endsection
+100
View File
@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ClearGame - Track Your Games. Level Up Your Backlog.</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
:root {
--primary: #7F00FF;
--accent: #00C9FF;
--background: #121212;
--text: #EAEAEA;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--background);
color: var(--text);
}
.gradient-text {
background: linear-gradient(90deg, var(--primary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.gradient-border {
border: 2px solid transparent;
background: linear-gradient(var(--background), var(--background)) padding-box,
linear-gradient(90deg, var(--primary), var(--accent)) border-box;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand text-2xl fw-bold gradient-text" href="#">ClearGame</a>
<div class="d-flex">
<a href="#" class="text-gray-300 text-decoration-none me-3">Login</a>
<a href="#" class="btn btn-gradient">Sign Up</a>
</div>
</div>
</nav>
<main class="container">
<section class="text-center py-5">
<h1 class="display-4 fw-bold mb-4">
<span class="gradient-text">Track Your Games.</span><br>
Level Up Your Backlog.
</h1>
<p class="lead text-gray-400 mb-5 mx-auto" style="max-width: 600px;">
Organize your gaming journey, discover new titles, and connect with fellow gamers.
Your ultimate companion for managing and completing your game collection.
</p>
<div class="d-flex justify-content-center gap-3">
<a href="#" class="btn btn-gradient btn-lg">Get Started</a>
<a href="#" class="btn btn-outline-gradient btn-lg">Learn More</a>
</div>
</section>
<section class="py-5">
<div class="row g-4">
<div class="col-md-4">
<div class="gradient-border p-4 rounded-3 h-100">
<div class="display-4 mb-3 gradient-text">🎮</div>
<h3 class="h4 fw-semibold mb-2">Track Your Progress</h3>
<p class="text-gray-400">Keep tabs on your gaming journey and celebrate your achievements.</p>
</div>
</div>
<div class="col-md-4">
<div class="gradient-border p-4 rounded-3 h-100">
<div class="display-4 mb-3 gradient-text">📊</div>
<h3 class="h4 fw-semibold mb-2">Discover New Games</h3>
<p class="text-gray-400">Find your next favorite game with personalized recommendations.</p>
</div>
</div>
<div class="col-md-4">
<div class="gradient-border p-4 rounded-3 h-100">
<div class="display-4 mb-3 gradient-text">👥</div>
<h3 class="h4 fw-semibold mb-2">Connect with Gamers</h3>
<p class="text-gray-400">Share your experiences and connect with like-minded gamers.</p>
</div>
</div>
</div>
</section>
<section class="text-center py-5">
<h2 class="display-5 fw-bold mb-4">Ready to Level Up Your Gaming Experience?</h2>
<a href="#" class="btn btn-gradient btn-lg">Join ClearGame Today</a>
</section>
</main>
<footer class="border-top border-gray-800 py-4">
<div class="container text-center text-gray-400">
<p class="mb-0">&copy; 2024 ClearGame. All rights reserved.</p>
</div>
</footer>
</body>
</html>
+129 -12
View File
@@ -5,32 +5,149 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<title>@yield('title', config('app.name', 'Laravel'))</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--gradient-start: #4f46e5;
--gradient-end: #7c3aed;
}
body {
background-color: #0f172a;
color: #e2e8f0;
}
.text-gradient {
background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.btn-gradient {
background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
border: none;
color: white;
}
.btn-gradient:hover {
background: linear-gradient(to right, var(--gradient-end), var(--gradient-start));
color: white;
}
.btn-outline-gradient {
border: 1px solid var(--gradient-start);
color: var(--gradient-start);
}
.btn-outline-gradient:hover {
background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
color: white;
}
.gradient-border {
border: 1px solid transparent;
background: linear-gradient(#0f172a, #0f172a) padding-box,
linear-gradient(to right, var(--gradient-start), var(--gradient-end)) border-box;
}
.navbar {
background-color: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(10px);
}
.dropdown-menu {
background-color: #1e293b;
border: 1px solid #334155;
}
.dropdown-item {
color: #e2e8f0;
}
.dropdown-item:hover {
background-color: #334155;
color: #e2e8f0;
}
.dropdown-divider {
border-top: 1px solid #334155;
}
</style>
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
@include('layouts.navigation')
<!-- Page Heading -->
@isset($header)
<header class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand text-2xl fw-bold gradient-text" href="/">ClearGame</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('games.index') ? 'active' : '' }}" href="{{ route('games.index') }}">
<i class="fas fa-gamepad me-1"></i> Games
</a>
</li>
</ul>
<div class="d-flex">
@auth
<div class="dropdown">
<button class="btn btn-outline-gradient dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-user me-1"></i> {{ Auth::user()->name }}
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="dropdown-item">
<i class="fas fa-sign-out-alt me-1"></i> Logout
</button>
</form>
</li>
</ul>
</div>
@else
<a href="{{ route('login') }}" class="text-gray-300 text-decoration-none me-3">
<i class="fas fa-sign-in-alt me-1"></i> Login
</a>
<a href="{{ route('register') }}" class="btn btn-gradient">
<i class="fas fa-user-plus me-1"></i> Sign Up
</a>
@endauth
</div>
</div>
</header>
@endisset
</div>
</nav>
<!-- Page Content -->
<main>
{{ $slot }}
<main class="container py-5">
@yield('content')
</main>
<footer class="border-top border-gray-800 py-4 mt-5">
<div class="container text-center text-gray-400">
<p class="mb-0">&copy; 2024 ClearGame. All rights reserved.</p>
</div>
</footer>
</div>
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
+56
View File
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title') - ClearGame</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand text-2xl fw-bold gradient-text" href="/">ClearGame</a>
</div>
</nav>
<main class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="gradient-border rounded-3 p-4">
<div class="text-center mb-4">
<h1 class="h3 fw-bold gradient-text">@yield('title')</h1>
<p class="text-gray-400">@yield('subtitle')</p>
</div>
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@yield('content')
</div>
</div>
</div>
</main>
<footer class="border-top border-gray-800 py-4 mt-5">
<div class="container text-center text-gray-400">
<p class="mb-0">&copy; 2024 ClearGame. All rights reserved.</p>
</div>
</footer>
</body>
</html>
+49
View File
@@ -0,0 +1,49 @@
@extends('layouts.app')
@section('title', 'Create New List')
@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">Create New List</h4>
</div>
<div class="card-body">
<form action="{{ route('lists.store') }}" method="POST">
@csrf
<div class="mb-3">
<label for="name" class="form-label">List Name</label>
<input type="text" class="form-control bg-dark text-white @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name') }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="description" class="form-label">Description (Optional)</label>
<textarea class="form-control bg-dark text-white @error('description') is-invalid @enderror"
id="description" name="description" rows="3">{{ old('description') }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-between align-items-center">
<a href="{{ route('lists.index') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Lists
</a>
<button type="submit" class="btn btn-gradient">
<i class="fas fa-save me-2"></i>Create List
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+50
View File
@@ -0,0 +1,50 @@
@extends('layouts.app')
@section('title', 'Edit List')
@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">Edit List</h4>
</div>
<div class="card-body">
<form action="{{ route('lists.update', $list) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="name" class="form-label">List Name</label>
<input type="text" class="form-control bg-dark text-white @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name', $list->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="description" class="form-label">Description (Optional)</label>
<textarea class="form-control bg-dark text-white @error('description') is-invalid @enderror"
id="description" name="description" rows="3">{{ old('description', $list->description) }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-between align-items-center">
<a href="{{ route('lists.show', $list) }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to List
</a>
<button type="submit" class="btn btn-gradient">
<i class="fas fa-save me-2"></i>Update List
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+54
View File
@@ -0,0 +1,54 @@
@extends('layouts.app')
@section('title', 'My Lists')
@section('content')
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="text-gradient">My Lists</h1>
<a href="{{ route('lists.create') }}" class="btn btn-gradient">
<i class="fas fa-plus me-2"></i>Create New List
</a>
</div>
<div class="row g-4">
@foreach($lists as $list)
<div class="col-md-6 col-lg-4">
<div class="card bg-dark text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<h5 class="card-title mb-0">{{ $list->name }}</h5>
<span class="badge bg-secondary">{{ $list->games_count }} games</span>
</div>
@if($list->description)
<p class="card-text text-muted">{{ $list->description }}</p>
@endif
</div>
<div class="card-footer bg-dark border-top border-secondary">
<div class="d-flex justify-content-between align-items-center">
<a href="{{ route('lists.show', $list) }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye me-1"></i> View
</a>
@if(!$list->is_default)
<div class="btn-group">
<a href="{{ route('lists.edit', $list) }}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-edit me-1"></i> Edit
</a>
<form action="{{ route('lists.destroy', $list) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-outline-danger btn-sm"
onclick="return confirm('Are you sure you want to delete this list?')">
<i class="fas fa-trash me-1"></i> Delete
</button>
</form>
</div>
@endif
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
@endsection
+76
View File
@@ -0,0 +1,76 @@
@extends('layouts.app')
@section('title', $list->name)
@section('content')
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="text-gradient mb-1">{{ $list->name }}</h1>
@if($list->description)
<p class="text-muted mb-0">{{ $list->description }}</p>
@endif
</div>
<div class="btn-group">
@if(!$list->is_default)
<a href="{{ route('lists.edit', $list) }}" class="btn btn-outline-secondary">
<i class="fas fa-edit me-2"></i>Edit List
</a>
@endif
<a href="{{ route('lists.index') }}" class="btn btn-outline-primary">
<i class="fas fa-list me-2"></i>All Lists
</a>
</div>
</div>
@if($games->isEmpty())
<div class="text-center py-5">
<i class="fas fa-gamepad fa-3x text-muted mb-3"></i>
<h3 class="text-muted">No games in this list yet</h3>
<p class="text-muted">Start adding games to your list!</p>
</div>
@else
<div class="row g-4">
@foreach($games as $game)
<div class="col-md-6 col-lg-4">
<div class="card bg-dark text-white h-100">
<img src="{{ $game->cover_url }}" class="card-img-top" alt="{{ $game->name }}"
style="height: 200px; object-fit: cover;">
<div class="card-body">
<h5 class="card-title">{{ $game->name }}</h5>
<div class="d-flex gap-2 mb-2">
@foreach($game->platforms as $platform)
<span class="badge bg-secondary">{{ $platform->name }}</span>
@endforeach
</div>
<div class="d-flex gap-2">
@foreach($game->genres as $genre)
<span class="badge bg-primary">{{ $genre->name }}</span>
@endforeach
</div>
</div>
<div class="card-footer bg-dark border-top border-secondary">
<div class="d-flex justify-content-between align-items-center">
<a href="{{ route('games.show', $game) }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye me-1"></i> View Game
</a>
<form action="{{ route('lists.removeGame', [$list, $game]) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-outline-danger btn-sm">
<i class="fas fa-times me-1"></i> Remove
</button>
</form>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div class="d-flex justify-content-center mt-4">
{{ $games->links() }}
</div>
@endif
</div>
@endsection
+206
View File
@@ -0,0 +1,206 @@
@csrf
<div class="mb-4">
<label for="content" class="form-label">Review</label>
<textarea name="content" id="content" rows="5"
class="form-control bg-dark text-white @error('content') is-invalid @enderror"
required>{{ old('content', $review->content ?? '') }}</textarea>
@error('content')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label class="form-label">Rating</label>
<div class="rating-container">
<div class="rating-stars d-flex gap-1">
@for($i = 1; $i <= 10; $i++)
<div class="rating-item" data-rating="{{ $i }}">
<input type="radio" name="rating" id="rating{{ $i }}" value="{{ $i }}"
class="rating-input" {{ old('rating', $review->rating ?? '') == $i ? 'checked' : '' }}>
<label for="rating{{ $i }}" class="rating-star">
<i class="fas fa-star"></i>
</label>
<span class="rating-number">{{ $i }}</span>
</div>
@endfor
</div>
<div class="rating-value mt-2 text-muted">
<span class="current-rating">{{ old('rating', $review->rating ?? '0') }}</span>/10
</div>
</div>
@error('rating')
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label class="form-label">Recommendation</label>
<div class="recommendation-buttons d-flex gap-3">
<input type="radio" name="recommended" id="recommended1" value="1" class="recommendation-input"
{{ old('recommended', $review->recommended ?? '') == 1 ? 'checked' : '' }}>
<label for="recommended1" class="recommendation-button btn btn-outline-success">
<i class="fas fa-thumbs-up me-2"></i> Recommended
</label>
<input type="radio" name="recommended" id="recommended0" value="0" class="recommendation-input"
{{ old('recommended', $review->recommended ?? '') == 0 ? 'checked' : '' }}>
<label for="recommended0" class="recommendation-button btn btn-outline-danger">
<i class="fas fa-thumbs-down me-2"></i> Not Recommended
</label>
</div>
@error('recommended')
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-gradient">
<i class="fas fa-paper-plane me-2"></i> {{ isset($review) ? 'Update Review' : 'Post Review' }}
</button>
@push('styles')
<style>
.rating-container {
display: flex;
flex-direction: column;
align-items: center;
}
.rating-stars {
font-size: 1.5rem;
position: relative;
}
.rating-item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.rating-input {
display: none;
}
.rating-star {
color: #6c757d;
cursor: pointer;
transition: color 0.2s;
}
.rating-number {
position: absolute;
top: -20px;
font-size: 0.8rem;
color: #6c757d;
opacity: 0;
transition: opacity 0.2s;
}
.rating-item:hover .rating-number {
opacity: 1;
}
.rating-item:hover .rating-star,
.rating-item:hover ~ .rating-item .rating-star,
.rating-input:checked ~ .rating-item .rating-star {
color: #ffc107;
}
.rating-input:checked + .rating-star {
color: #ffc107;
}
.rating-value {
font-size: 1rem;
font-weight: 500;
}
.recommendation-input {
display: none;
}
.recommendation-button {
flex: 1;
text-align: center;
transition: all 0.2s;
}
.recommendation-button:hover {
transform: scale(1.05);
}
.recommendation-input:checked + .recommendation-button {
background-color: var(--bs-btn-bg);
color: var(--bs-btn-color);
}
#recommended1:checked + .recommendation-button {
background-color: var(--bs-success);
border-color: var(--bs-success);
}
#recommended0:checked + .recommendation-button {
background-color: var(--bs-danger);
border-color: var(--bs-danger);
}
</style>
@endpush
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const ratingContainer = document.querySelector('.rating-stars');
const currentRating = document.querySelector('.current-rating');
const ratingInputs = document.querySelectorAll('.rating-input');
ratingContainer.addEventListener('mouseover', function(e) {
const ratingItem = e.target.closest('.rating-item');
if (!ratingItem) return;
const rating = ratingItem.dataset.rating;
currentRating.textContent = rating;
// Highlight stars up to the hovered rating
const stars = document.querySelectorAll('.rating-star');
stars.forEach((star, index) => {
if (index < rating) {
star.style.color = '#ffc107';
} else {
star.style.color = '#6c757d';
}
});
});
ratingContainer.addEventListener('mouseout', function() {
const checkedInput = document.querySelector('.rating-input:checked');
if (checkedInput) {
currentRating.textContent = checkedInput.value;
// Restore the checked state colors
const stars = document.querySelectorAll('.rating-star');
stars.forEach((star, index) => {
if (index < checkedInput.value) {
star.style.color = '#ffc107';
} else {
star.style.color = '#6c757d';
}
});
} else {
currentRating.textContent = '0';
// Reset all stars to default color
const stars = document.querySelectorAll('.rating-star');
stars.forEach(star => {
star.style.color = '#6c757d';
});
}
});
// Update current rating when a star is clicked
ratingInputs.forEach(input => {
input.addEventListener('change', function() {
currentRating.textContent = this.value;
});
});
});
</script>
@endpush
+31
View File
@@ -0,0 +1,31 @@
<div class="gradient-border rounded-3 p-4 mb-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h3 class="h5 mb-1">{{ $review->user->name }}</h3>
<div class="d-flex gap-2 mb-2">
<span class="badge {{ $review->recommended ? 'bg-success' : 'bg-danger' }}">
{{ $review->recommended ? 'Recommended' : 'Not Recommended' }}
</span>
<span class="badge bg-primary">{{ $review->rating }}/10</span>
</div>
</div>
<small class="text-gray-400">{{ $review->created_at->diffForHumans() }}</small>
</div>
<p class="text-gray-400 mb-0">{{ $review->content }}</p>
@can('update', $review)
<div class="mt-3">
<a href="{{ route('reviews.edit', $review->id) }}" class="btn btn-sm btn-outline-gradient me-2">
Edit
</a>
<form action="{{ route('reviews.destroy', $review->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-outline-gradient" onclick="return confirm('Are you sure?')">
Delete
</button>
</form>
</div>
@endcan
</div>
+19
View File
@@ -0,0 +1,19 @@
@extends('layouts.app')
@section('title', 'Write a Review - ' . $game->name)
@section('content')
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="gradient-border rounded-3 p-4">
<h1 class="h3 mb-4">Write a Review for {{ $game->name }}</h1>
<form action="{{ route('games.reviews.store', $game->slug) }}" method="POST">
@include('reviews._form')
</form>
</div>
</div>
</div>
</div>
@endsection
+20
View File
@@ -0,0 +1,20 @@
@extends('layouts.app')
@section('title', 'Edit Review - ' . $game->name)
@section('content')
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="gradient-border rounded-3 p-4">
<h1 class="h3 mb-4">Edit Your Review for {{ $game->name }}</h1>
<form action="{{ route('reviews.update', $review->id) }}" method="POST">
@method('PUT')
@include('reviews._form')
</form>
</div>
</div>
</div>
</div>
@endsection