2026-02-06 14:56:56 +02:00
@ extends ( 'layouts.app' )
@ section ( 'content' )
< div class = "container" >
< div class = "row justify-content-center" >
< div class = "col-md-10" >
@ if ( session ( 'success' ))
< div class = "alert alert-success alert-dismissible fade show" role = "alert" >
{{ session ( 'success' ) }}
< button type = "button" class = "btn-close" data - bs - dismiss = "alert" aria - label = "Close" ></ button >
</ div >
@ endif
< div class = "card" >
< div class = "card-body" >
<!-- Product Header -->
< div class = "d-flex justify-content-between align-items-start mb-4" >
< div class = "flex-grow-1" >
< h1 class = "mb-2" > {{ $product -> title }} </ h1 >
< p class = "text-muted mb-0" >
< span class = "badge bg-secondary" > {{ $product -> category -> name }} </ span >
@ if ( $product -> isPending ())
< span class = "badge bg-warning text-dark" > Pending Review </ span >
@ elseif ( $product -> isApproved ())
< span class = "badge bg-success" > Approved </ span >
@ elseif ( $product -> isRejected ())
< span class = "badge bg-danger" > Rejected </ span >
@ endif
</ p >
</ div >
2026-02-06 15:13:20 +02:00
< div class = "d-flex align-items-center gap-2" >
@ auth
<!-- Upvote Button -->
< button
class = "btn btn-outline-primary upvote-btn {{ $product->isUpvotedBy (auth()->user()) ? 'active' : '' }}"
data - product - id = "{{ $product->id }}"
data - upvoted = "{{ $product->isUpvotedBy (auth()->user()) ? 'true' : 'false' }}" >
< i class = "bi bi-arrow-up-circle" ></ i >
< span class = "upvote-count" > {{ $product -> upvotes () -> count () }} </ span >
</ button >
<!-- Bookmark Button -->
< button
class = "btn btn-outline-secondary bookmark-btn {{ $product->isBookmarkedBy (auth()->user()) ? 'active' : '' }}"
data - product - id = "{{ $product->id }}"
data - bookmarked = "{{ $product->isBookmarkedBy (auth()->user()) ? 'true' : 'false' }}" >
< i class = "bi {{ $product->isBookmarkedBy (auth()->user()) ? 'bi-bookmark-fill' : 'bi-bookmark' }}" ></ i >
</ button >
@ else
< div class = "text-muted" >
< i class = "bi bi-arrow-up-circle" ></ i >
< span > {{ $product -> upvotes () -> count () }} </ span > upvotes
</ div >
@ endauth
2026-02-06 14:56:56 +02:00
@ can ( 'edit-product' , $product )
< a href = "{{ route('products.edit', $product ) }}" class = "btn btn-sm btn-outline-primary" > Edit </ a >
@ endcan
@ can ( 'delete-product' , $product )
< form action = "{{ route('products.destroy', $product ) }}" method = "POST" class = "d-inline" onsubmit = "return confirm('Are you sure you want to delete this product?');" >
@ csrf
@ method ( 'DELETE' )
< button type = "submit" class = "btn btn-sm btn-outline-danger" > Delete </ button >
</ form >
@ endcan
</ div >
</ div >
<!-- Product Thumbnail -->
@ if ( $product -> thumbnail_url )
< div class = "mb-4" >
< img src = "{{ $product->thumbnail_url }}" alt = "{{ $product->title }}" class = "img-fluid rounded" style = "max-height: 400px;" >
</ div >
@ endif
<!-- Product Description -->
< div class = "mb-4" >
< h4 > About </ h4 >
< p class = "lead" > {{ $product -> description }} </ p >
</ div >
<!-- Product Links -->
< div class = "mb-4" >
< a href = "{{ $product->website_url }}" target = "_blank" rel = "noopener" class = "btn btn-primary btn-lg" >
Visit Website →
</ a >
</ div >
<!-- Product Metadata -->
< hr >
< div class = "row text-muted small" >
< div class = "col-md-6" >
< strong > Submitted by :</ strong >
< a href = "{{ route('profile.show', $product->user ) }}" > {{ $product -> user -> name }} </ a >
</ div >
< div class = "col-md-6 text-md-end" >
< strong > Submitted :</ strong > {{ $product -> created_at -> format ( 'F j, Y' ) }}
</ div >
</ div >
@ if ( $product -> isApproved () && $product -> approved_at )
< div class = "row text-muted small mt-2" >
< div class = "col-md-12" >
< strong > Approved :</ strong > {{ $product -> approved_at -> format ( 'F j, Y' ) }}
</ div >
</ div >
@ endif
</ div >
</ div >
2026-02-06 15:13:20 +02:00
<!-- Comments Section -->
< div class = "card mt-3" >
< div class = "card-header" >
< h5 class = "mb-0" > Comments ({{ $product -> comments () -> count () }}) </ h5 >
</ div >
< div class = "card-body" >
@ auth
<!-- Comment Form -->
< form action = "{{ route('products.comments.store', $product ) }}" method = "POST" class = "mb-4" >
@ csrf
< div class = "mb-3" >
< textarea
class = "form-control @error('body') is-invalid @enderror"
name = "body"
rows = "3"
placeholder = "Share your thoughts about this product..."
required
maxlength = "1000" > {{ old ( 'body' ) }} </ textarea >
@ error ( 'body' )
< span class = "invalid-feedback" role = "alert" >
< strong > {{ $message }} </ strong >
</ span >
@ enderror
</ div >
< button type = "submit" class = "btn btn-primary" > Post Comment </ button >
</ form >
@ else
< p class = "text-muted" >
< a href = "{{ route('login') }}" > Login </ a > to post a comment .
</ p >
@ endauth
<!-- Comments List -->
@ forelse ( $product -> comments as $comment )
< div class = "border-top pt-3 mt-3" >
< div class = "d-flex" >
< div class = "me-3" >
@ if ( $comment -> user -> avatar_url )
< img src = "{{ $comment->user ->avatar_url }}" alt = "{{ $comment->user ->name }}" class = "rounded-circle" style = "width: 40px; height: 40px; object-fit: cover;" >
@ else
< div class = "bg-secondary text-white rounded-circle d-flex align-items-center justify-content-center" style = "width: 40px; height: 40px;" >
{{ strtoupper ( substr ( $comment -> user -> name , 0 , 1 )) }}
</ div >
@ endif
</ div >
< div class = "flex-grow-1" >
< div class = "d-flex justify-content-between align-items-start" >
< div >
< strong > {{ $comment -> user -> name }} </ strong >
< span class = "text-muted small" > · {{ $comment -> created_at -> diffForHumans () }} </ span >
</ div >
@ can ( 'delete-comment' , $comment )
< form action = "{{ route('comments.destroy', $comment ) }}" method = "POST" class = "d-inline" >
@ csrf
@ method ( 'DELETE' )
< button type = "submit" class = "btn btn-sm btn-link text-danger p-0" onclick = "return confirm('Are you sure you want to delete this comment?');" >
Delete
</ button >
</ form >
@ endcan
</ div >
< p class = "mb-0 mt-2" > {{ $comment -> body }} </ p >
</ div >
</ div >
</ div >
@ empty
< p class = "text-muted text-center py-4" > No comments yet . Be the first to comment !</ p >
@ endforelse
</ div >
</ div >
2026-02-06 14:56:56 +02:00
<!-- Moderator Actions -->
@ can ( 'approve-product' )
@ if ( $product -> isPending () || $product -> isRejected ())
< div class = "card mt-3" >
< div class = "card-header bg-warning" >
< h5 class = "mb-0" > Moderator Actions </ h5 >
</ div >
< div class = "card-body" >
< form action = "{{ route('products.approve', $product ) }}" method = "POST" class = "d-inline" >
@ csrf
< button type = "submit" class = "btn btn-success" > Approve Product </ button >
</ form >
< form action = "{{ route('products.reject', $product ) }}" method = "POST" class = "d-inline" >
@ csrf
< button type = "submit" class = "btn btn-danger" onclick = "return confirm('Are you sure you want to reject this product?');" > Reject Product </ button >
</ form >
</ div >
</ div >
@ endif
@ endcan
< div class = "mt-3" >
< a href = "{{ route('products.index') }}" class = "btn btn-secondary" > ← Back to Products </ a >
</ div >
</ div >
</ div >
</ div >
2026-02-06 15:13:20 +02:00
@ push ( 'styles' )
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" >
@ endpush
@ push ( 'scripts' )
< script >
// Upvote functionality
document . querySelectorAll ( '.upvote-btn' ) . forEach ( button => {
button . addEventListener ( 'click' , async function () {
const productId = this . getAttribute ( 'data-product-id' );
const isUpvoted = this . getAttribute ( 'data-upvoted' ) === 'true' ;
try {
const response = await fetch ( `/products/${productId}/upvote` , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/json' ,
'Accept' : 'application/json' ,
},
});
const data = await response . json ();
if ( data . success ) {
// Update button state
this . setAttribute ( 'data-upvoted' , data . upvoted ? 'true' : 'false' );
if ( data . upvoted ) {
this . classList . add ( 'active' );
} else {
this . classList . remove ( 'active' );
}
// Update count
this . querySelector ( '.upvote-count' ) . textContent = data . upvotesCount ;
}
} catch ( error ) {
console . error ( 'Error toggling upvote:' , error );
}
});
});
// Bookmark functionality
document . querySelectorAll ( '.bookmark-btn' ) . forEach ( button => {
button . addEventListener ( 'click' , async function () {
const productId = this . getAttribute ( 'data-product-id' );
const isBookmarked = this . getAttribute ( 'data-bookmarked' ) === 'true' ;
try {
const response = await fetch ( `/products/${productId}/bookmark` , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/json' ,
'Accept' : 'application/json' ,
},
});
const data = await response . json ();
if ( data . success ) {
// Update button state
this . setAttribute ( 'data-bookmarked' , data . bookmarked ? 'true' : 'false' );
const icon = this . querySelector ( 'i' );
if ( data . bookmarked ) {
this . classList . add ( 'active' );
icon . classList . remove ( 'bi-bookmark' );
icon . classList . add ( 'bi-bookmark-fill' );
} else {
this . classList . remove ( 'active' );
icon . classList . remove ( 'bi-bookmark-fill' );
icon . classList . add ( 'bi-bookmark' );
}
}
} catch ( error ) {
console . error ( 'Error toggling bookmark:' , error );
}
});
});
</ script >
@ endpush
2026-02-06 14:56:56 +02:00
@ endsection