mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-13 04:30:41 -05:00
Merge pull request 'setup mod index page' (#20) from waffle.lord/forge:impl/mod-listing-page into mod-listing-page
Reviewed-on: SPT/forge#20
This commit is contained in:
commit
411514148b
@ -15,7 +15,7 @@ class ModController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('viewAny', Mod::class);
|
$this->authorize('viewAny', Mod::class);
|
||||||
|
|
||||||
return ModResource::collection(Mod::all());
|
return view('mod.index');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(ModRequest $request)
|
public function store(ModRequest $request)
|
||||||
|
71
app/Livewire/ModIndex.php
Normal file
71
app/Livewire/ModIndex.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\Mod;
|
||||||
|
use App\Models\SptVersion;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
class ModIndex extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
|
||||||
|
public string $modSearch = '';
|
||||||
|
|
||||||
|
public string $sectionFilter = 'featured';
|
||||||
|
|
||||||
|
public int $versionFilter = -1;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
// 'featured' section is default
|
||||||
|
$section = 'featured';
|
||||||
|
|
||||||
|
switch ($this->sectionFilter) {
|
||||||
|
case 'new':
|
||||||
|
$section = 'created_at';
|
||||||
|
break;
|
||||||
|
case 'most_downloaded':
|
||||||
|
$section = 'total_downloads';
|
||||||
|
break;
|
||||||
|
case 'recently_updated':
|
||||||
|
$section = 'updated_at';
|
||||||
|
break;
|
||||||
|
case 'top_rated':
|
||||||
|
// probably use some kind of 'likes' or something
|
||||||
|
// not implemented yet afaik -waffle
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mods = Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
|
||||||
|
->withTotalDownloads()
|
||||||
|
->with(['users:id,name'])
|
||||||
|
->where('name', 'like', '%'.Str::trim($this->modSearch).'%');
|
||||||
|
|
||||||
|
if ($this->versionFilter === -1) {
|
||||||
|
$mods = $mods
|
||||||
|
->with(['latestVersion', 'latestVersion.sptVersion'])
|
||||||
|
->whereHas('latestVersion.sptVersion');
|
||||||
|
} else {
|
||||||
|
$mods = $mods->with(['latestVersion' => function ($query) {
|
||||||
|
$query->where('spt_version_id', $this->versionFilter);
|
||||||
|
}, 'latestVersion.sptVersion'])
|
||||||
|
->whereHas('latestVersion.sptVersion', function ($query) {
|
||||||
|
$query->where('spt_version_id', $this->versionFilter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$mods = $mods->orderByDesc($section)->paginate(12);
|
||||||
|
|
||||||
|
$sptVersions = SptVersion::select(['id', 'version', 'color_class'])->orderByDesc('version')->get();
|
||||||
|
|
||||||
|
return view('livewire.mod-index', ['mods' => $mods, 'sptVersions' => $sptVersions]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeSection($section): void
|
||||||
|
{
|
||||||
|
$this->sectionFilter = $section;
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,9 @@ class ModPolicy
|
|||||||
/**
|
/**
|
||||||
* Determine whether the user can view multiple models.
|
* Determine whether the user can view multiple models.
|
||||||
*/
|
*/
|
||||||
public function viewAny(User $user): bool
|
public function viewAny(?User $user): bool
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
31
resources/views/components/mod-card.blade.php
Normal file
31
resources/views/components/mod-card.blade.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@props(['mod', 'versionScope' => 'latestVersion'])
|
||||||
|
|
||||||
|
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}" class="mod-list-component mx-auto w-full max-w-md md:max-w-2xl">
|
||||||
|
<div class="flex flex-col group h-full w-full bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl overflow-hidden hover:shadow-lg hover:bg-gray-50 dark:hover:bg-black hover:shadow-gray-400 dark:hover:shadow-black transition-all duration-200">
|
||||||
|
<div class="h-auto md:h-full md:flex">
|
||||||
|
<div class="h-auto md:h-full md:shrink-0 overflow-hidden">
|
||||||
|
@if (empty($mod->thumbnail))
|
||||||
|
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="block dark:hidden h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
||||||
|
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="hidden dark:block h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
||||||
|
@else
|
||||||
|
<img src="{{ $mod->thumbnailUrl }}" alt="{{ $mod->name }}" class="h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col w-full justify-between p-5">
|
||||||
|
<div class="pb-3">
|
||||||
|
<div class="flex justify-between items-center space-x-3">
|
||||||
|
<h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3>
|
||||||
|
<span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
|
||||||
|
{{ $mod->{$versionScope}->sptVersion->version }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm italic text-slate-600 dark:text-gray-200">
|
||||||
|
By {{ $mod->users->pluck('name')->implode(', ') }}
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ Str::limit($mod->teaser, 100) }}</p>
|
||||||
|
</div>
|
||||||
|
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
@ -3,35 +3,7 @@
|
|||||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||||
@foreach ($mods as $mod)
|
@foreach ($mods as $mod)
|
||||||
@if ($mod->{$versionScope})
|
@if ($mod->{$versionScope})
|
||||||
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}" class="mod-list-component mx-auto w-full max-w-md md:max-w-2xl">
|
<x-mod-card :mod="$mod" :versionScope="$versionScope"/>
|
||||||
<div class="flex flex-col group h-full w-full bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl overflow-hidden hover:shadow-lg hover:bg-gray-50 dark:hover:bg-black hover:shadow-gray-400 dark:hover:shadow-black transition-all duration-200">
|
|
||||||
<div class="h-auto md:h-full md:flex">
|
|
||||||
<div class="h-auto md:h-full md:shrink-0 overflow-hidden">
|
|
||||||
@if (empty($mod->thumbnail))
|
|
||||||
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="block dark:hidden h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
|
||||||
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="hidden dark:block h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
|
||||||
@else
|
|
||||||
<img src="{{ $mod->thumbnailUrl }}" alt="{{ $mod->name }}" class="h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col w-full justify-between p-5">
|
|
||||||
<div class="pb-3">
|
|
||||||
<div class="flex justify-between items-center space-x-3">
|
|
||||||
<h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3>
|
|
||||||
<span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
|
|
||||||
{{ $mod->{$versionScope}->sptVersion->version }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-sm italic text-slate-600 dark:text-gray-200">
|
|
||||||
By {{ $mod->users->pluck('name')->implode(', ') }}
|
|
||||||
</p>
|
|
||||||
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ Str::limit($mod->teaser, 100) }}</p>
|
|
||||||
</div>
|
|
||||||
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
121
resources/views/livewire/mod-index.blade.php
Normal file
121
resources/views/livewire/mod-index.blade.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<div>
|
||||||
|
{{-- page links --}}
|
||||||
|
<div class="m-6">
|
||||||
|
{{ $mods->links() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- grid layout --}}
|
||||||
|
<div class="grid gap-6 grid-cols-1 lg:grid-cols-4 m-4">
|
||||||
|
|
||||||
|
{{-- column 1 --}}
|
||||||
|
<div class="col-span-3">
|
||||||
|
{{-- mods serach bar --}}
|
||||||
|
<div>
|
||||||
|
<search class="relative group">
|
||||||
|
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
|
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M771-593 630-734l-85 84-85-84 113-114q12-12 27-17.5t30-5.5q16 0 30.5 5.5T686-848l85 85q18 17 26.5 39.5T806-678q0 23-8.5 45T771-593ZM220-409q-18-18-18-42.5t18-42.5l98-99 85 85-99 99q-17 18-41.5 18T220-409Zm-43 297q-11-12-17-26.5t-6-30.5q0-16 5.5-30.5T177-226l283-282-127-128q-18-17-18-41.5t18-42.5q17-18 42-18t43 18l127 127 57-57 112 114q12 12 12 28t-12 28q-12 12-28 12t-28-12L290-112q-12 12-26.5 17.5T234-89q-15 0-30-6t-27-17Z"/></svg>
|
||||||
|
</div>
|
||||||
|
<input wire:model.live="modSearch" class="sm:w-1/3 w-full rounded-md border-0 bg-white dark:bg-gray-700 py-1.5 pl-10 pr-3 text-gray-900 dark:text-gray-300 ring-1 ring-inset ring-gray-300 dark:ring-gray-700 placeholder:text-gray-400 dark:placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 dark:focus:bg-gray-200 dark:focus:text-black dark:focus:ring-0 sm:text-sm sm:leading-6" placeholder="Search Mods ..." />
|
||||||
|
</search>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- mobile section filters --}}
|
||||||
|
<div class="flex flex-col sm:hidden my-4">
|
||||||
|
<select wire:model.live="sectionFilter" class="block w-full rounded-md dark:text-white bg-gray-100 dark:bg-gray-950 border-gray-300 dark:border-gray-700 focus:border-grey-500 dark:focus:border-grey-600 focus:ring-grey-500 dark:focus:ring-grey-600">
|
||||||
|
<option value="featured">{{__('Featured')}}</option>
|
||||||
|
<option value="new">{{__('New')}}</option>
|
||||||
|
<option value="recently_updated">{{__('Recently Updated')}}</option>
|
||||||
|
<option value="most_downloaded">{{__('Most Downloaded')}}</option>
|
||||||
|
<option value="top_rated">{{__('Top Rated')}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- section filters --}}
|
||||||
|
<div class="hidden sm:block my-4">
|
||||||
|
<nav class="isolate flex divide-x divide-gray-200 dark:divide-gray-800 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl" aria-label="Tabs">
|
||||||
|
<button wire:click="changeSection('featured')" class="tab rounded-l-xl group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||||
|
<span>{{ __('Featured') }}</span>
|
||||||
|
<span aria-hidden="true" class="{{ $sectionFilter === 'featured' ? 'bg-gray-500 absolute inset-x-0 bottom-0 h-0.5' : 'bottom-0 h-0.5' }}"></span>
|
||||||
|
</button>
|
||||||
|
<button wire:click="changeSection('new')" class="tab group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||||
|
<span>{{ __('New') }}</span>
|
||||||
|
<span aria-hidden="true" class="{{ $sectionFilter === 'new' ? 'bg-gray-500 absolute inset-x-0 bottom-0 h-0.5' : 'bottom-0 h-0.5' }}"></span>
|
||||||
|
</button>
|
||||||
|
<button wire:click="changeSection('recently_updated')" class="tab group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||||
|
<span>{{ __('Recently Updated') }}</span>
|
||||||
|
<span aria-hidden="true" class="{{ $sectionFilter === 'recently_updated' ? 'bg-gray-500 absolute inset-x-0 bottom-0 h-0.5' : 'bottom-0 h-0.5' }}"></span>
|
||||||
|
</button>
|
||||||
|
<button wire:click="changeSection('most_downloaded')" class="tab group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||||
|
<span>{{ __('Most Downloaded') }}</span>
|
||||||
|
<span aria-hidden="true" class="{{ $sectionFilter === 'most_downloaded' ? 'bg-gray-500 absolute inset-x-0 bottom-0 h-0.5' : 'bottom-0 h-0.5' }}"></span>
|
||||||
|
</button>
|
||||||
|
<button wire:click="changeSection('top_rated')" class="tab rounded-r-xl group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||||
|
<span>{{ __('Top Rated') }}</span>
|
||||||
|
<span aria-hidden="true" class="{{ $sectionFilter === 'top_rated' ? 'bg-gray-500 absolute inset-x-0 bottom-0 h-0.5' : 'bottom-0 h-0.5' }}"></span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- mobile version filters --}}
|
||||||
|
<div class="flex flex-col lg:hidden my-4">
|
||||||
|
<select wire:model.live="versionFilter" class="rounded-md dark:text-white bg-gray-100 dark:bg-gray-950 border-gray-300 dark:border-gray-700 focus:border-grey-500 dark:focus:border-grey-600 focus:ring-grey-500 dark:focus:ring-grey-600">
|
||||||
|
<option value="-1">Any Version</option>
|
||||||
|
@foreach($sptVersions as $version)
|
||||||
|
<option value="{{ $version->id }}">{{ $version->version }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- mobile tags filters --}}
|
||||||
|
<div class="flex flex-col lg:hidden my-4">
|
||||||
|
<p class="text-gray-700 dark:text-gray-200">tags filters here when ready :)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- mod cards --}}
|
||||||
|
<div class="grid gap-6 grid-cols-2">
|
||||||
|
@foreach($mods as $mod)
|
||||||
|
<x-mod-card :mod="$mod" />
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- column 2 --}}
|
||||||
|
<div class="flex flex-col col-span-1 gap-4 hidden lg:flex">
|
||||||
|
{{-- spt version filters --}}
|
||||||
|
<div class="flex flex-col drop-shadow-2xl text-gray-700 bg-white dark:text-gray-200 dark:bg-gray-950 p-4 rounded-xl">
|
||||||
|
<h2>SPT Version</h2>
|
||||||
|
<select wire:model.live="versionFilter" class="rounded-md dark:text-white bg-gray-100 dark:bg-gray-950 border-gray-300 dark:border-gray-700 focus:border-grey-500 dark:focus:border-grey-600 focus:ring-grey-500 dark:focus:ring-grey-600">
|
||||||
|
<option value="-1">Any Version</option>
|
||||||
|
@foreach($sptVersions as $version)
|
||||||
|
<option value="{{ $version->id }}">{{ $version->version }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- tag filters --}}
|
||||||
|
<div class="flex flex-col drop-shadow-2xl text-gray-700 bg-white dark:text-gray-200 dark:bg-gray-950 p-4 rounded-xl gap-2">
|
||||||
|
<h2>Tags</h2>
|
||||||
|
<button class="flex justify-between text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-700 rounded-md p-2">
|
||||||
|
<span>Placeholder</span>
|
||||||
|
<span>2501</span>
|
||||||
|
</button>
|
||||||
|
<button class="flex justify-between text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-700 rounded-md p-2">
|
||||||
|
<span>Stuff</span>
|
||||||
|
<span>420</span>
|
||||||
|
</button>
|
||||||
|
<button class="flex justify-between text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-700 rounded-md p-2">
|
||||||
|
<span>Here</span>
|
||||||
|
<span>69</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{-- page links --}}
|
||||||
|
<div class="m-6">
|
||||||
|
{{ $mods->links() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
4
resources/views/mod/index.blade.php
Normal file
4
resources/views/mod/index.blade.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<x-app-layout>
|
||||||
|
@livewire('mod-index')
|
||||||
|
</x-app-layout>
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user