mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 10:31:11 -05:00
- Add RideReviewComponent for submitting reviews - Star rating input with real-time validation - Rate limiting and anti-spam measures - Edit capabilities for own reviews - Add RideReviewListComponent for displaying reviews - Paginated list with sort/filter options - Helpful vote functionality - Statistics display with rating distribution - Add ReviewModerationComponent for review management - Review queue with status filters - Approve/reject functionality - Batch actions support - Edit capabilities - Update Memory Bank documentation - Document component implementations - Track feature completion - Update technical decisions
170 lines
3.8 KiB
PHP
170 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Models\Review;
|
|
use App\Models\Ride;
|
|
use App\Models\HelpfulVote;
|
|
use App\Enums\ReviewStatus;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
|
|
class RideReviewListComponent extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
/**
|
|
* The ride whose reviews are being displayed
|
|
*/
|
|
public Ride $ride;
|
|
|
|
/**
|
|
* Current sort field
|
|
*/
|
|
public string $sortField = 'created_at';
|
|
|
|
/**
|
|
* Current sort direction
|
|
*/
|
|
public string $sortDirection = 'desc';
|
|
|
|
/**
|
|
* Rating filter
|
|
*/
|
|
public ?int $ratingFilter = null;
|
|
|
|
/**
|
|
* Success/error message
|
|
*/
|
|
public ?string $message = null;
|
|
|
|
/**
|
|
* Whether to show the statistics panel
|
|
*/
|
|
public bool $showStats = true;
|
|
|
|
/**
|
|
* Listeners for events
|
|
*/
|
|
protected $listeners = [
|
|
'review-saved' => '$refresh',
|
|
];
|
|
|
|
/**
|
|
* Mount the component
|
|
*/
|
|
public function mount(Ride $ride)
|
|
{
|
|
$this->ride = $ride;
|
|
}
|
|
|
|
/**
|
|
* Toggle sort field
|
|
*/
|
|
public function sortBy(string $field)
|
|
{
|
|
if ($this->sortField === $field) {
|
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
$this->sortField = $field;
|
|
$this->sortDirection = 'desc';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filter by rating
|
|
*/
|
|
public function filterByRating(?int $rating)
|
|
{
|
|
$this->ratingFilter = $rating === $this->ratingFilter ? null : $rating;
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Toggle helpful vote
|
|
*/
|
|
public function toggleHelpfulVote(Review $review)
|
|
{
|
|
if (!Auth::check()) {
|
|
$this->message = 'You must be logged in to vote on reviews.';
|
|
return;
|
|
}
|
|
|
|
// Rate limiting
|
|
$key = 'vote_' . Auth::id();
|
|
if (RateLimiter::tooManyAttempts($key, 10)) { // 10 attempts per minute
|
|
$this->message = 'Please wait before voting again.';
|
|
return;
|
|
}
|
|
RateLimiter::hit($key);
|
|
|
|
try {
|
|
HelpfulVote::toggle($review->id, Auth::id());
|
|
$this->message = 'Vote recorded successfully.';
|
|
} catch (\Exception $e) {
|
|
$this->message = 'An error occurred while recording your vote.';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle statistics panel
|
|
*/
|
|
public function toggleStats()
|
|
{
|
|
$this->showStats = !$this->showStats;
|
|
}
|
|
|
|
/**
|
|
* Get review statistics
|
|
*/
|
|
public function getStatistics()
|
|
{
|
|
$reviews = $this->ride->reviews()->approved();
|
|
|
|
return [
|
|
'total' => $reviews->count(),
|
|
'average' => round($reviews->avg('rating'), 1),
|
|
'distribution' => [
|
|
5 => $reviews->where('rating', 5)->count(),
|
|
4 => $reviews->where('rating', 4)->count(),
|
|
3 => $reviews->where('rating', 3)->count(),
|
|
2 => $reviews->where('rating', 2)->count(),
|
|
1 => $reviews->where('rating', 1)->count(),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the reviews query
|
|
*/
|
|
protected function getReviewsQuery()
|
|
{
|
|
$query = $this->ride->reviews()
|
|
->with(['user', 'helpfulVotes'])
|
|
->approved();
|
|
|
|
// Apply rating filter
|
|
if ($this->ratingFilter) {
|
|
$query->where('rating', $this->ratingFilter);
|
|
}
|
|
|
|
// Apply sorting
|
|
$query->orderBy($this->sortField, $this->sortDirection);
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Render the component
|
|
*/
|
|
public function render()
|
|
{
|
|
return view('livewire.ride-review-list-component', [
|
|
'reviews' => $this->getReviewsQuery()->paginate(10),
|
|
'statistics' => $this->getStatistics(),
|
|
]);
|
|
}
|
|
}
|