Worked on homepage mod components

This commit is contained in:
Refringe 2024-05-17 23:54:03 -04:00
parent 8d5920468d
commit 54fd2ede53
Signed by: Refringe
GPG Key ID: 7715B85B4A6306ED
22 changed files with 270 additions and 101 deletions

View File

@ -0,0 +1,17 @@
<?php
namespace App\Helpers;
class ColorHelper
{
public static function tagColorClasses($color): string
{
return match ($color)
{
'red' => 'bg-red-50 text-red-700 ring-red-600/20',
'green' => 'bg-green-50 text-green-700 ring-green-600/20',
'yellow' => 'bg-yellow-50 text-yellow-700 ring-yellow-600/20',
default => 'bg-gray-50 text-gray-700 ring-gray-600/20',
};
}
}

View File

@ -38,9 +38,14 @@ class Mod extends Model
return $this->hasMany(ModVersion::class);
}
public function versionWithHighestSptVersion()
public function versionLastUpdated()
{
return $this->hasOne(ModVersion::class)->highestSptVersion();
return $this->hasOne(ModVersion::class)->lastUpdated();
}
public function versionLatestSptVersion()
{
return $this->hasOne(ModVersion::class)->latestSptVersion();
}
protected function slug(): Attribute

View File

@ -2,6 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -30,9 +31,14 @@ class ModVersion extends Model
return $this->belongsTo(SptVersion::class);
}
public function scopeHighestSptVersion($query)
public function scopeLastUpdated(Builder $query): void
{
return $query->orderByDesc(
$query->orderByDesc('created_at');
}
public function scopeLatestSptVersion(Builder $query): void
{
$query->orderByDesc(
SptVersion::select('spt_versions.version')->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
)->orderByDesc('mod_versions.version');
}

View File

@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider

View File

@ -1,17 +0,0 @@
<?php
namespace App\View\Components;
use App\Models\Mod;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class LatestModListing extends Component
{
public function render(): View
{
return view('components.latest-mod-listing', [
'latestMods' => Mod::with('versionWithHighestSptVersion.sptVersion')->latest()->take(6)->get(),
]);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\View\Components;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
use App\Helpers\ColorHelper;
class ModList extends Component
{
public $mods;
public string $versionScope;
public function __construct($mods, $versionScope)
{
$this->mods = $mods;
$this->versionScope = $versionScope;
foreach ($this->mods as $mod) {
$color = $mod->{$this->versionScope}->sptVersion->color_class;
$mod->colorClass = ColorHelper::tagColorClasses($color);
}
}
public function render(): View
{
return view('components.mod-list', [
'mods' => $this->mods,
'versionScope' => $this->versionScope,
]);
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\View\Components;
use App\Models\Mod;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\View\Component;
class ModListSection extends Component
{
public Collection $modsSuggested;
public Collection $modsLatest;
public Collection $modsUpdated;
public function __construct()
{
$this->modsSuggested = $this->fetchSuggestedMods();
$this->modsLatest = $this->fetchLatestMods();
$this->modsUpdated = $this->fetchUpdatedMods();
}
private function fetchSuggestedMods(): Collection
{
return Mod::with('versionLatestSptVersion.sptVersion')->whereSuggested(true)->take(6)->get();
}
private function fetchLatestMods(): Collection
{
return Mod::with('versionLatestSptVersion.sptVersion')->latest()->take(6)->get();
}
private function fetchUpdatedMods(): Collection
{
return Mod::with('versionLastUpdated.sptVersion')->take(6)->get();
}
public function getSections(): array
{
return [
[
'title' => 'Suggested Mods',
'mods' => $this->modsSuggested,
'versionScope' => 'versionLatestSptVersion'
],
[
'title' => 'Latest Mods',
'mods' => $this->modsLatest,
'versionScope' => 'versionLatestSptVersion'
],
[
'title' => 'Recently Updated Mods',
'mods' => $this->modsUpdated,
'versionScope' => 'versionLastUpdated'
],
];
}
public function render(): View
{
return view('components.mod-list-section', [
'sections' => $this->getSections(),
]);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\View\Components;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class ModVersionTag extends Component
{
public string $tagColor;
public function __construct($tagColor)
{
$this->tagColor = $tagColor;
}
public function render(): View
{
return view('components.mod-version-tag');
}
public function tagClasses($tagColor): string
{
return match ($this->tagColor) {
'red' => 'bg-red-50 text-red-700 ring-red-600/20',
'green' => 'bg-green-50 text-green-700 ring-green-600/20',
'yellow' => 'bg-yellow-50 text-yellow-700 ring-yellow-600/20',
default => 'bg-gray-50 text-gray-700 ring-gray-600/20',
};
}
}

View File

@ -29,7 +29,10 @@
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"psr-4": {

22
composer.lock generated
View File

@ -1424,16 +1424,16 @@
},
{
"name": "inertiajs/inertia-laravel",
"version": "v1.0.0",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/inertiajs/inertia-laravel.git",
"reference": "fcf3d6db1a259a55d8d18cf43fc971202c1f6b0d"
"reference": "576fba4da6f2ba6348ddf57a750c73231904d598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/fcf3d6db1a259a55d8d18cf43fc971202c1f6b0d",
"reference": "fcf3d6db1a259a55d8d18cf43fc971202c1f6b0d",
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/576fba4da6f2ba6348ddf57a750c73231904d598",
"reference": "576fba4da6f2ba6348ddf57a750c73231904d598",
"shasum": ""
},
"require": {
@ -1487,7 +1487,7 @@
],
"support": {
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
"source": "https://github.com/inertiajs/inertia-laravel/tree/v1.0.0"
"source": "https://github.com/inertiajs/inertia-laravel/tree/v1.1.0"
},
"funding": [
{
@ -1495,7 +1495,7 @@
"type": "github"
}
],
"time": "2024-03-09T00:30:58+00:00"
"time": "2024-05-16T01:41:06+00:00"
},
{
"name": "laravel/fortify",
@ -7675,16 +7675,16 @@
},
{
"name": "mockery/mockery",
"version": "1.6.11",
"version": "1.6.12",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "81a161d0b135df89951abd52296adf97deb0723d"
"reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d",
"reference": "81a161d0b135df89951abd52296adf97deb0723d",
"url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
"reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
"shasum": ""
},
"require": {
@ -7754,7 +7754,7 @@
"security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery"
},
"time": "2024-03-21T18:34:15+00:00"
"time": "2024-05-16T03:13:13+00:00"
},
{
"name": "myclabs/deep-copy",

View File

@ -23,6 +23,7 @@ class ModFactory extends Factory
'description' => $this->faker->paragraph,
'license_id' => License::factory(),
'source_code_link' => $this->faker->url(),
'suggested' => $this->faker->boolean,
'contains_ai_content' => $this->faker->boolean,
'created_at' => now(),
'updated_at' => now(),

View File

@ -18,6 +18,7 @@ return new class extends Migration
$table->longText('description');
$table->foreignIdFor(License::class)->constrained('licenses');
$table->string('source_code_link');
$table->boolean('suggested')->default(false);
$table->boolean('contains_ai_content')->default(false);
$table->softDeletes();
$table->timestamps();

32
package-lock.json generated
View File

@ -932,9 +932,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001617",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz",
"integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==",
"version": "1.0.30001620",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz",
"integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==",
"dev": true,
"funding": [
{
@ -1080,9 +1080,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.4.764",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.764.tgz",
"integrity": "sha512-ZXbPV46Y4dNCA+k7YHB+BYlzcoMtZ1yH6V0tQ1ul0wmA7RiwJfS29LSdRlE1myWBXRzEgm/Lz6tryj5WVQiLmg==",
"version": "1.4.774",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz",
"integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==",
"dev": true
},
"node_modules/emoji-regex": {
@ -1416,9 +1416,9 @@
}
},
"node_modules/laravel-vite-plugin": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.2.tgz",
"integrity": "sha512-Mcclml10khYzBVxDwJro8wnVDwD4i7XOSEMACQNnarvTnHjrjXLLL+B/Snif2wYAyElsOqagJZ7VAinb/2vF5g==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.4.tgz",
"integrity": "sha512-dEj8Q/Fsn0kKbOQ55bl/NmyJL+dD6OxnVaM/nNByw5XV4b00ky6FzXKVuHLDr4BvSJKH1y6oaOcEG5wKpCZ5+A==",
"dev": true,
"dependencies": {
"picocolors": "^1.0.0",
@ -1655,9 +1655,9 @@
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true
},
"node_modules/picomatch": {
@ -2335,9 +2335,9 @@
"dev": true
},
"node_modules/update-browserslist-db": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
"integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
"integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
"dev": true,
"funding": [
{
@ -2355,7 +2355,7 @@
],
"dependencies": {
"escalade": "^3.1.2",
"picocolors": "^1.0.0"
"picocolors": "^1.0.1"
},
"bin": {
"update-browserslist-db": "cli.js"

View File

@ -1,48 +0,0 @@
<div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<div class="md:flex md:items-center md:justify-between border-b pb-4 mb-6">
<div class="min-w-0 flex-1">
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">{{ __('Latest Mods') }}</h2>
</div>
<div class="mt-4 flex md:ml-4 md:mt-0">
<button type="button" class="ml-3 inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">{{ __('View All') }}</button>
</div>
</div>
<ul role="list" class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
@foreach ($latestMods as $mod)
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow">
<div class="flex w-full items-center justify-between space-x-6 p-6">
<img class="h-10 w-10 flex-shrink-0 rounded-[5px] bg-gray-300" src="https://placehold.co/300x300/EEE/31343C?font=open-sans&text=MOD" alt="">
<div class="flex-1 truncate">
<div class="flex items-center space-x-3">
<h3 class="truncate text-sm font-medium text-gray-900">{{ $mod->name }}</h3>
<span class="inline-flex flex-shrink-0 items-center rounded-full bg-{{ $mod->versionWithHighestSptVersion->sptVersion->color_class }}-50 px-1.5 py-0.5 text-xs font-medium text-{{ $mod->versionWithHighestSptVersion->sptVersion->color_class }}-700 ring-1 ring-inset ring-{{ $mod->versionWithHighestSptVersion->sptVersion->color_class }}-600/20">{{ $mod->versionWithHighestSptVersion->sptVersion->version }}</span>
</div>
<p class="mt-1 truncate text-sm text-gray-500">{{ $mod->description }}</p>
</div>
</div>
<div>
<div class="-mt-px flex divide-x divide-gray-200">
<div class="flex w-0 flex-1">
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}" class="relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-t-0 border-transparent py-4 text-sm font-semibold text-gray-900">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/>
</svg>
{{ __('View Details') }}
</a>
</div>
<div class="-ml-px flex w-0 flex-1">
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}/download" class="relative inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-br-lg border border-t-0 border-transparent py-4 text-sm font-semibold text-gray-900">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9.75v6.75m0 0-3-3m3 3 3-3m-8.25 6a4.5 4.5 0 0 1-1.41-8.775 5.25 5.25 0 0 1 10.233-2.33 3 3 0 0 1 3.758 3.848A3.752 3.752 0 0 1 18 19.5H6.75Z"/>
</svg>
{{ __('Download') }}
</a>
</div>
</div>
</div>
</li>
@endforeach
</ul>
</div>

View File

@ -0,0 +1,6 @@
@props(['mods'])
<div class="mx-auto max-w-7xl px-4 pt-16 sm:px-6 lg:px-8">
<x-page-content-title :title="$title" button-text="View All" button-link="/mods" />
<x-mod-list :mods="$mods" :versionScope="$versionScope" />
</div>

View File

@ -0,0 +1,7 @@
@foreach ($sections as $section)
@include('components.mod-list-section-partial', [
'title' => $section['title'],
'mods' => $section['mods'],
'versionScope' => $section['versionScope'],
])
@endforeach

View File

@ -0,0 +1,40 @@
@props(['mods', 'versionScope'])
<ul role="list" {{ $attributes->class(['grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3']) }}>
@foreach ($mods as $mod)
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow">
<div class="flex w-full items-center justify-between space-x-6 p-4">
<img class="h-16 w-16 flex-shrink-0 rounded-[5px] bg-gray-300" src="https://placehold.co/300x300/EEE/31343C?font=open-sans&text=MOD" alt="">
<div class="flex-1 truncate">
<div class="flex items-center space-x-3">
<h3 class="truncate text-sm font-medium text-gray-900">{{ $mod->name }}</h3>
<span class="{{ $mod->colorClass }} inline-flex flex-shrink-0 items-center rounded-full px-1.5 py-0.5 text-xs font-medium ring-1 ring-inset">
{{ $mod->{$versionScope}->sptVersion->version }}
</span>
</div>
<p class="mt-1 truncate text-sm text-gray-500">{{ $mod->description }}</p>
</div>
</div>
<div>
<div class="-mt-px flex divide-x divide-gray-200">
<div class="flex w-0 flex-1">
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}" class="relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-t-0 border-transparent py-4 text-sm font-semibold text-gray-900">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/>
</svg>
{{ __('View Details') }}
</a>
</div>
<div class="-ml-px flex w-0 flex-1">
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}/download" class="relative inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-br-lg border border-t-0 border-transparent py-4 text-sm font-semibold text-gray-900">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9.75v6.75m0 0-3-3m3 3 3-3m-8.25 6a4.5 4.5 0 0 1-1.41-8.775 5.25 5.25 0 0 1 10.233-2.33 3 3 0 0 1 3.758 3.848A3.752 3.752 0 0 1 18 19.5H6.75Z"/>
</svg>
{{ __('Download') }}
</a>
</div>
</div>
</div>
</li>
@endforeach
</ul>

View File

@ -0,0 +1,12 @@
<div {{ $attributes->class(['md:flex md:items-center md:justify-between border-b pb-4 mb-6']) }}>
<div class="min-w-0 flex-1">
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">{{ __($title) }}</h2>
</div>
@if (isset($buttonText) && isset($buttonLink))
<div class="mt-4 flex md:ml-4 md:mt-0">
<a href="{{ $buttonLink }}">
<button type="button" class="ml-3 inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">{{ __($buttonText) }}</button>
</a>
</div>
@endif
</div>

View File

@ -1,6 +1,6 @@
<x-app-layout>
<div class="py-12">
<div class="pt-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
{{-- Welcome Section --}}
@ -21,7 +21,9 @@
</div>
</div>
<x-latest-mod-listing/>
<div class="pb-10">
<x-mod-list-section/>
</div>
</div>
</div>
</div>

View File

@ -20,6 +20,7 @@
<body class="font-sans antialiased">
<x-warning/>
<x-banner/>
<div class="min-h-screen bg-gray-100">

View File

@ -5,7 +5,7 @@
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="{{ route('dashboard') }}">
<a href="{{ route('home') }}">
<x-application-mark class="block h-9 w-auto"/>
</a>
</div>

View File

@ -4,7 +4,11 @@ use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('home');
});
})->name('home');
Route::get('/mods', function () {
return 'list all mods';
})->name('mods');
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
Route::get('/dashboard', function () {