feat: leaderboard with top (by period) and new tabs
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
25e1dd2413
commit
3f8fa9f22f
@@ -19,6 +19,10 @@
|
||||
class="inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium {{ request()->routeIs('products.*') ? 'border-primary-500 text-gray-900 dark:text-white' : 'border-transparent text-gray-500 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-600 hover:text-gray-700 dark:hover:text-gray-300' }}">
|
||||
Products
|
||||
</a>
|
||||
<a href="{{ route('leaderboard') }}"
|
||||
class="inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium {{ request()->routeIs('leaderboard') ? 'border-primary-500 text-gray-900 dark:text-white' : 'border-transparent text-gray-500 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-600 hover:text-gray-700 dark:hover:text-gray-200' }}">
|
||||
Leaderboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Search Bar (Desktop) -->
|
||||
@@ -230,6 +234,10 @@
|
||||
class="block pl-3 pr-4 py-2 border-l-4 text-base font-medium {{ request()->routeIs('products.index') ? 'border-primary-500 text-primary-700 bg-primary-50' : 'border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700' }}">
|
||||
Discover
|
||||
</a>
|
||||
<a href="{{ route('leaderboard') }}"
|
||||
class="block pl-3 pr-4 py-2 border-l-4 text-base font-medium {{ request()->routeIs('leaderboard') ? 'border-primary-500 text-primary-700 dark:text-primary-200 bg-primary-50 dark:bg-primary-900' : 'border-transparent text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 hover:text-gray-700 dark:hover:text-gray-200' }}">
|
||||
Leaderboard
|
||||
</a>
|
||||
@auth
|
||||
@if(auth()->user()->hasRole('admin'))
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@php
|
||||
$title = 'Leaderboard - ' . config('app.name');
|
||||
$pill = fn (bool $active) => $active
|
||||
? 'bg-primary-600 text-white border-primary-600'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700';
|
||||
$tabClass = fn (bool $active) => $active
|
||||
? 'border-primary-500 text-gray-900 dark:text-white'
|
||||
: 'border-transparent text-gray-500 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-600 hover:text-gray-700 dark:hover:text-gray-200';
|
||||
$periodLabel = ['today' => 'today', 'week' => 'this week', 'month' => 'this month'][$period] ?? null;
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Leaderboard</h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">The most upvoted and the newest products from European makers.</p>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="mt-6 flex space-x-8 border-b border-gray-200 dark:border-gray-700">
|
||||
<a href="{{ route('leaderboard', ['tab' => 'top', 'period' => $period]) }}"
|
||||
class="inline-flex items-center px-1 pb-3 border-b-2 text-sm font-medium {{ $tabClass($tab === 'top') }}">Top</a>
|
||||
<a href="{{ route('leaderboard', ['tab' => 'new']) }}"
|
||||
class="inline-flex items-center px-1 pb-3 border-b-2 text-sm font-medium {{ $tabClass($tab === 'new') }}">New</a>
|
||||
</div>
|
||||
|
||||
<!-- Period pills -->
|
||||
@if($tab === 'top')
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
@foreach($periods as $key => $label)
|
||||
<a href="{{ route('leaderboard', ['tab' => 'top', 'period' => $key]) }}"
|
||||
class="inline-flex items-center px-3 py-1 rounded-full border text-sm font-medium {{ $pill($period === $key) }}">{{ $label }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- List -->
|
||||
@if($products->isEmpty())
|
||||
<div class="mt-8 text-center py-12 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
@if($tab === 'new')
|
||||
<p class="text-gray-600 dark:text-gray-400">No products yet.</p>
|
||||
@else
|
||||
<p class="text-gray-900 dark:text-white font-medium">No upvotes yet {{ $periodLabel ?? 'at all' }}</p>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Try a longer period.</p>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<ol class="mt-6 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@foreach($products as $product)
|
||||
<li class="flex items-center gap-4 p-4">
|
||||
<span data-rank="{{ $products->firstItem() + $loop->index }}"
|
||||
class="w-8 text-xl font-bold text-gray-400 dark:text-gray-500 text-center">{{ $products->firstItem() + $loop->index }}</span>
|
||||
|
||||
<a href="{{ route('products.show', $product) }}" class="flex-shrink-0">
|
||||
@if($product->thumbnail_url)
|
||||
<img src="{{ $product->thumbnail_url }}" alt="{{ $product->title }}" class="h-12 w-12 rounded-lg object-cover" loading="lazy">
|
||||
@else
|
||||
<div class="h-12 w-12 rounded-lg bg-gradient-to-br from-primary-100 to-primary-200 dark:from-primary-900 dark:to-primary-800 flex items-center justify-center text-primary-700 dark:text-primary-200 font-semibold">
|
||||
{{ strtoupper(substr($product->title, 0, 1)) }}
|
||||
</div>
|
||||
@endif
|
||||
</a>
|
||||
|
||||
<div class="flex-1 min-w-0">
|
||||
<a href="{{ route('products.show', $product) }}" class="text-base font-semibold text-gray-900 dark:text-white hover:text-primary-600 dark:hover:text-primary-400">{{ $product->title }}</a>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 truncate">{{ Str::limit($product->description, 100) }}</p>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
@foreach($product->tags->take(3) as $tag)
|
||||
<span>#{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
<x-product.badges :product="$product" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-shrink-0 text-center">
|
||||
@include('components.product.upvote-button', ['product' => $product, 'size' => 'md'])
|
||||
@if($tab === 'top' && $periodLabel)
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ $product->period_upvotes_count }} {{ $periodLabel }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
|
||||
<div class="mt-6">{{ $products->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user