feat: product badges and show page layout

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Doar Mihai
2026-10-17 15:56:58 +03:00
co-authored by Claude Sonnet 5.5
parent 029689407b
commit 9920fafab6
12 changed files with 382 additions and 63 deletions
@@ -0,0 +1,15 @@
@props(['product'])
@php
$ticked = collect(\App\Models\Product::BADGES)->filter(fn ($label, $key) => $product->{$key});
@endphp
@if($ticked->isNotEmpty())
<div class="flex flex-wrap items-center gap-1.5">
@foreach($ticked as $key => $label)
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-primary-100 dark:bg-primary-900 text-primary-800 dark:text-primary-200">
{{ $label }}
</span>
@endforeach
</div>
@endif
@@ -37,6 +37,11 @@
{{ Str::limit($product->description, 100) }}
</p>
<!-- Badges -->
<div class="mb-3 empty:hidden">
<x-product.badges :product="$product" />
</div>
<!-- Meta Information -->
<div class="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
<div class="flex items-center space-x-3">
+20
View File
@@ -133,6 +133,26 @@
</div>
@endif
<!-- Badges -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
Badges
</label>
<div class="mt-1 grid grid-cols-2 sm:grid-cols-4 gap-3">
@foreach(\App\Models\Product::BADGES as $key => $label)
<label class="flex items-center p-3 border border-gray-300 rounded-md">
<input type="hidden" name="{{ $key }}" value="0">
<input type="checkbox"
name="{{ $key }}"
value="1"
{{ old($key, false) ? 'checked' : '' }}
class="focus:ring-primary-500 h-4 w-4 text-primary-600 border-gray-300 rounded">
<span class="ml-2 text-sm text-gray-900">{{ $label }}</span>
</label>
@endforeach
</div>
</div>
<!-- Thumbnail -->
<div>
<label for="thumbnail" class="block text-sm font-medium text-gray-700">
+28
View File
@@ -139,6 +139,34 @@
</div>
@endif
@php
$badgesLocked = $product->status === 'approved' && !auth()->user()->hasAnyRole(['moderator', 'admin']);
@endphp
<!-- Badges -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
Badges
</label>
<div class="mt-1 grid grid-cols-2 sm:grid-cols-4 gap-3">
@foreach(\App\Models\Product::BADGES as $key => $label)
<label class="flex items-center p-3 border border-gray-300 rounded-md">
<input type="hidden" name="{{ $key }}" value="0">
<input type="checkbox"
name="{{ $key }}"
value="1"
{{ old($key, $product->{$key}) ? 'checked' : '' }}
{{ $badgesLocked ? 'disabled' : '' }}
class="focus:ring-primary-500 h-4 w-4 text-primary-600 border-gray-300 rounded">
<span class="ml-2 text-sm text-gray-900">{{ $label }}</span>
</label>
@endforeach
</div>
@if($badgesLocked)
<p class="mt-2 text-sm text-gray-500">Locked after approval</p>
@endif
</div>
<!-- Current Thumbnail -->
@if($product->thumbnail_url)
<div>
+88 -60
View File
@@ -53,6 +53,18 @@
<span>•</span>
<span>{{ $product->created_at->format('M j, Y') }}</span>
</div>
<div class="mt-3">
<x-product.badges :product="$product" />
</div>
@if($product->tags->isNotEmpty())
<div class="mt-3 flex flex-wrap gap-x-3 gap-y-1 text-sm text-gray-500 dark:text-gray-400">
@foreach($product->tags as $tag)
<span>#{{ $tag->name }}</span>
@endforeach
</div>
@endif
</div>
<!-- Action Buttons -->
@@ -105,69 +117,78 @@
</div>
</div>
</div>
<!-- Product Content -->
<div class="px-6 py-8">
<!-- Product Thumbnail -->
@if($product->thumbnail_url)
<div class="mb-8 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700">
<img src="{{ $product->thumbnail_url }}"
alt="{{ $product->title }}"
class="w-full h-auto max-h-96 object-contain bg-gray-50 dark:bg-gray-900">
</div>
@endif
<!-- Product Description -->
<div class="mb-8">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">About</h2>
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed whitespace-pre-line">{{ $product->description }}</p>
</div>
<!-- Visit Website Button -->
<div class="mb-8">
<a href="{{ $product->website_url }}"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 shadow-sm">
Visit Website
<svg class="ml-2 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
</a>
</div>
<!-- Product Metadata -->
@if($product->isApproved() && $product->approved_at)
<div class="pt-6 border-t border-gray-200 dark:border-gray-700">
<dl class="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-2">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Submitted by</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
<a href="{{ route('profile.show', $product->user) }}" class="hover:text-primary-600 dark:hover:text-primary-400">
{{ $product->user->name }}
</a>
</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Submitted on</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->created_at->format('F j, Y') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Approved on</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->approved_at->format('F j, Y') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Category</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->category->name }}</dd>
</div>
</dl>
</div>
@endif
</div>
</div>
<!-- Gallery -->
<!-- slot:gallery -->
<div class="mt-6 grid grid-cols-1 lg:grid-cols-4 gap-6">
<!-- Main column -->
<div class="lg:col-span-3 space-y-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 px-6 py-8">
<!-- Product Thumbnail -->
@if($product->thumbnail_url)
<div class="mb-8 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700">
<img src="{{ $product->thumbnail_url }}"
alt="{{ $product->title }}"
class="w-full h-auto max-h-96 object-contain bg-gray-50 dark:bg-gray-900">
</div>
@endif
<!-- Product Description -->
<div class="mb-8">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">About</h2>
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed whitespace-pre-line">{{ $product->description }}</p>
</div>
<!-- Visit Website Button -->
<div class="mb-8">
<a href="{{ $product->website_url }}"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 shadow-sm">
Visit Website
<svg class="ml-2 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
</a>
</div>
<!-- Product Metadata -->
@if($product->isApproved() && $product->approved_at)
<div class="pt-6 border-t border-gray-200 dark:border-gray-700">
<dl class="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-2">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Submitted by</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
<a href="{{ route('profile.show', $product->user) }}" class="hover:text-primary-600 dark:hover:text-primary-400">
{{ $product->user->name }}
</a>
</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Submitted on</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->created_at->format('F j, Y') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Approved on</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->approved_at->format('F j, Y') }}</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Category</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{{ $product->category->name }}</dd>
</div>
</dl>
</div>
@endif
</div>
<!-- slot:pricing -->
<!-- slot:alternatives -->
<!-- Comments Section -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 mt-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">
Comments ({{ $product->comments()->count() }})
@@ -259,6 +280,13 @@
</div>
</div>
</div>
</div>
<!-- Sidebar column -->
<div class="lg:col-span-1 space-y-6">
<!-- slot:team -->
</div>
</div>
<!-- Moderator Actions -->
@can('approve-product')