feat: icon list + search functionality

This commit is contained in:
Rico van Zelst
2023-11-15 13:58:12 +01:00
parent cc2ebc8899
commit cea55e776c
25 changed files with 402 additions and 41 deletions

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers;
use App\Models\SummonerIcon;
use Illuminate\Http\Request;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;
class SummonerIconController extends Controller
{
public function index()
{
$icons = QueryBuilder::for(SummonerIcon::class)
->allowedFilters('title')
->defaultSort('icon_id')
->paginate(72)
->appends(request()->query());
return view('icons.index', compact('icons'));
}
public function store(Request $request)
{
$request->validate([
'icon_id' => ['required', 'integer'],
'title' => ['nullable'],
'release_year' => ['nullable', 'integer'],
'legacy' => ['required'],
'image' => ['required'],
'esports_team' => ['nullable'],
'esports_region' => ['nullable'],
'esports_event' => ['nullable'],
'description' => ['nullable'],
]);
return SummonerIcon::create($request->validated());
}
public function show(SummonerIcon $summonerIcon)
{
return $summonerIcon;
}
public function update(Request $request, SummonerIcon $summonerIcon)
{
$request->validate([
'icon_id' => ['required', 'integer'],
'title' => ['nullable'],
'release_year' => ['nullable', 'integer'],
'legacy' => ['required'],
'image' => ['required'],
'esports_team' => ['nullable'],
'esports_region' => ['nullable'],
'esports_event' => ['nullable'],
'description' => ['nullable'],
]);
$summonerIcon->update($request->validated());
return $summonerIcon;
}
public function destroy(SummonerIcon $summonerIcon)
{
$summonerIcon->delete();
return response()->json();
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
class SummonerIcon extends Model
{
use Sluggable;
protected $fillable = [
'icon_id',
'title',
'description',
'release_year',
'legacy',
'image',
'esports_team',
'esports_region',
'esports_event',
];
protected $casts = [
'legacy' => 'boolean',
];
public function sluggable(): array
{
return [
'slug' => [
'source' => ['title', 'icon_id'],
],
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\View\Components\Icons;
use App\Models\SummonerIcon;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class List_all extends Component
{
public function __construct(public SummonerIcon $icons)
{
}
public function render(): View
{
return view('components.icons.list_all');
}
}

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('summoner_icons', function (Blueprint $table) {
$table->id();
$table->integer('icon_id');
$table->string('title')->nullable();
$table->string('description')->nullable();
$table->integer('release_year')->nullable();
$table->boolean('legacy');
$table->string('image');
$table->string('esports_team')->nullable();
$table->string('esports_region')->nullable();
$table->string('esports_event')->nullable();
$table->string('slug')->unique();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('summoner_icons');
}
};

View File

@@ -0,0 +1,64 @@
<?php
namespace Database\Seeders;
use App\Models\SummonerIcon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
class SummonerIconSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$iconDataUrl = 'https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/summoner-icons.json';
$iconData = json_decode(file_get_contents($iconDataUrl), true);
$changeCount = 0;
foreach ($iconData as $icon) {
$iconId = $icon['id'];
$iconExists = SummonerIcon::where('icon_id', $iconId)->first();
$iconAttributes = [
'icon_id' => $icon['id'],
'title' => $icon['title'],
'description' => $icon['descriptions'][0]['description'] ?? null,
'release_year' => $icon['yearReleased'],
'legacy' => $icon['isLegacy'],
'image' => 'https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/profile-icons/' . $icon['id'] . '.jpg',
'esports_team' => $icon['esportsTeam'] ?? null,
'esports_region' => $icon['esportsRegion'] ?? null,
'esports_event' => $icon['esportsEvent'] ?? null,
];
// Check if the champion already exists and if any attributes have changed, if so update the champion. If the champion doesn't exist, create it.
// This is to prevent the champion data from being updated every time the seeder is run. As I'll probably run this on a cron job.
if ($iconExists && $this->hasAttributesChanged($iconExists, $iconAttributes)) {
Log::info('Icon ' . $iconId . ' has changed, updating...');
$iconExists->update($iconAttributes);
$changeCount++;
} elseif (!$iconExists) {
Log::info('New icon detected! Creating ' . $iconId . '...');
SummonerIcon::create($iconAttributes);
$changeCount++;
}
}
if ($changeCount > 0) {
Artisan::call('cloudflare:purge');
}
}
private function hasAttributesChanged($champion, $attributes): bool
{
foreach ($attributes as $key => $value) {
if ($champion->{$key} != $value) {
return true;
}
}
return false;
}
}

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -2,7 +2,7 @@
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/icons/mstile-150x150.png"/>
<square150x150logo src="/img/icons/mstile-150x150.png"/>
<TileColor>#ff7c47</TileColor>
</tile>
</msapplication>

View File

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 862 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -3,12 +3,12 @@
"short_name": "Heimerdinger.lol",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",
"src": "/img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/android-chrome-512x512.png",
"src": "/img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}

View File

@@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/site.webmanifest">
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>Heimerdinger.LoL Champions</title>

View File

@@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/site.webmanifest">
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>{{$champion->name}} Heimerdinger.LoL</title>

View File

@@ -0,0 +1,50 @@
<?php
/** @var App\Models\SummonerIcon $icon */ ?>
<section class="max-w-screen-xl mx-auto mt-12">
<h2
class="text-3xl font-bold text-center text-transparent uppercase sm:text-4xl
bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text">
Summoner Icons</h2>
<x-icons.searchbar/>
<div class="container mx-auto p-4 flex items-center justify-center mt-3">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-12">
@foreach($icons as $key => $icon)
<div
class="champ-card flex flex-col text-gray-700 bg-stone-800/40 shadow-md rounded-2xl bg-clip-border
border border-stone-800 hover:border-orange-500/10 hover:shadow-orange-500/10 items-center">
<div
class="mx-4 overflow-hidden h-24 w-24 rounded-2xl bg-clip-border border-2 border-orange-400/40 mt-3">
<img @if($key < 8) loading="eager" @else loading="lazy" @endif
src="//wsrv.nl/?url={{ $icon->image }}&w=200&output=webp&q=50&il&default=ssl:wsrv.nl%2F%3Furl%3Dhttps://i.ibb.co/5s6YyvN/aaaa.png"
class="object-cover w-full h-full "
alt="{{ $icon->title }} Icon"
/>
</div>
<div class="px-4 py-2">
<div class="flex items-center justify-between">
<p class="block text-sm antialiased font-medium text-gray-100 text-center">
{{ $icon->title }}
</p>
</div>
</div>
<div
class="mb-2 px-4 flex justify-center items-end text-white text-2xl md:text-lg mt-auto">
<p class="font-medium text-sm hover:text-orange-400 "><a
href="/icon/{{$icon->slug}}">More details
<x-iconsax-bul-arrow-circle-right class="inline-block w-6"/>
</a>
</p>
</div>
</div>
@endforeach
</div>
</div>
{{ $icons->links() }}
</section>

View File

@@ -0,0 +1,25 @@
<div class="flex items-center justify-center mt-8 ">
<form action="{{ route('icons.index') }}" method="GET" class="flex" id="searchForm">
<div class="relative">
<input type="text" name="filter[title]" placeholder="Search by icon title"
value="{{ request('filter.title') }}"
class="border border-transparent focus:border-transparent focus:ring-0 border-stone-700 rounded-l px-4 py-2 bg-stone-800 text-white ring-orange-500 pr-10">
@if(request('filter.title'))
<button type="button" onclick="clearSearchAndSubmit()"
class="absolute inset-y-0 right-0 flex items-center px-3 bg-stone-800 text-white cursor-pointer">
<x-iconsax-lin-clipboard-close class="w-6 text-white"/>
</button>
@endif
</div>
<button type="submit"
class="bg-orange-500 hover:bg-orange-600 text-white font-semibold px-4 py-2 rounded-r focus:outline-none ring-orange-500">
Search
</button>
</form>
</div>
<script>
function clearSearchAndSubmit() {
document.querySelector('input[name="filter[title]"]').value = '';
document.getElementById('searchForm').submit();
}
</script>

View File

@@ -14,7 +14,8 @@
<span class="flex items-center justify-center">
View
champion
info <x-iconsax-bul-arrow-square-right class="w-5"/>
info
<x-iconsax-bul-arrow-square-right class="w-5"/>
</span>
</a>

View File

@@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/site.webmanifest">
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>Heimerdinger.LoL Home</title>
@@ -40,7 +40,7 @@
<link rel="preconnect" href="https://rsms.me/">
<link rel="preload" href="https://rsms.me/inter/inter.css" as="style">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" media="print" onload="this.media='all'">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>Heimerdinger.LoL Icons</title>
<meta name="description"
content="Explore all LoL icons on Heimerdinger.LoL. Find detailed information on popular summoner icons such as Debonair Rose, Omen of the Cursed Revenant, Lil' Sprout, Dominion Retirement and more!">
<!-- OpenGraph -->
<meta property="og:site_name" content="Heimerdinger.LoL">
<meta property="og:title" content="Heimerdinger.LoL • Icons">
<meta property="og:description"
content="Explore all LoL icons on Heimerdinger.LoL. Find detailed information on popular summoner icons such as Debonair Rose, Omen of the Cursed Revenant, Lil' Sprout, Dominion Retirement and more!">
<meta property="og:locale" content="en">
<meta property="og:type" content="website">
<meta property="og:image" content="{{asset('img/og_image.png')}}">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="heimerdinger.lol">
<meta property="twitter:title" content="Heimerdinger.LoL • Icons">
<meta property="twitter:description"
content="Explore all LoL icons on Heimerdinger.LoL. Find detailed information on popular summoner icons such as Debonair Rose, Omen of the Cursed Revenant, Lil' Sprout, Dominion Retirement and more!">
<meta property="twitter:image" content="{{asset('img/og_image.png')}}">
<link rel="preconnect" href="https://rsms.me/">
<link rel="preload" href="https://rsms.me/inter/inter.css" as="style">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" media="print" onload="this.media='all'">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="antialiased bg-stone-900 dark scroll-smooth">
<x-navbar/>
<x-icons.list_all :icons="$icons"/>
<x-footer/>
</body>
</html>

View File

@@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/site.webmanifest">
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>Heimerdinger.LoL Skins</title>

View File

@@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
<link rel="manifest" href="/icons/site.webmanifest">
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/icons/browserconfig.xml">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>{{$skin->skin_name}} Heimerdinger.LoL</title>

View File

@@ -4,6 +4,7 @@ use App\Http\Controllers\ChampionController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\ChampionSkinController;
use App\Http\Controllers\SummonerIconController;
/*
|--------------------------------------------------------------------------
@@ -23,4 +24,13 @@ Route::get('/champions', [ChampionController::class, 'index']);
Route::get('/champion/{champion}', [ChampionController::class, 'show']);
// Skins
Route::get('/skins', [ChampionSkinController::class, 'index'])->name('skins.index');
Route::get('/skin/{championSkin}', [ChampionSkinController::class, 'show']);
Route::get(
'/skin/{championSkin}',
[ChampionSkinController::class, 'show']
);
// Icons
Route::get('/icons', [
SummonerIconController::class,
'index'
])->name('icons.index');