mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-23 13:11:10 -05:00
feat: implement ride review components
- 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
This commit is contained in:
169
app/Livewire/RideReviewListComponent.php
Normal file
169
app/Livewire/RideReviewListComponent.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user