Files
thrillwiki_laravel/app/Livewire/RideReviewComponent.php
pacnpal 487c0e5866 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
2025-02-25 21:59:22 -05:00

152 lines
4.1 KiB
PHP

<?php
namespace App\Livewire;
use App\Models\Review;
use App\Models\Ride;
use App\Enums\ReviewStatus;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Livewire\Component;
use Livewire\Attributes\Rule;
class RideReviewComponent extends Component
{
/**
* The ride being reviewed
*/
public Ride $ride;
/**
* The review being edited (if in edit mode)
*/
public ?Review $review = null;
/**
* Form fields
*/
#[Rule('required|integer|min:1|max:5')]
public int $rating = 3;
#[Rule('nullable|string|max:100')]
public ?string $title = null;
#[Rule('required|string|min:10|max:2000')]
public string $content = '';
/**
* Whether the component is in edit mode
*/
public bool $isEditing = false;
/**
* Success/error message
*/
public ?string $message = null;
/**
* Mount the component
*/
public function mount(Ride $ride, ?Review $review = null)
{
$this->ride = $ride;
if ($review) {
$this->review = $review;
$this->isEditing = true;
$this->rating = $review->rating;
$this->title = $review->title;
$this->content = $review->content;
}
}
/**
* Save or update the review
*/
public function save()
{
// Check if user is authenticated
if (!Auth::check()) {
$this->message = 'You must be logged in to submit a review.';
return;
}
// Rate limiting
$key = 'review_' . Auth::id();
if (RateLimiter::tooManyAttempts($key, 5)) { // 5 attempts per minute
$this->message = 'Please wait before submitting another review.';
return;
}
RateLimiter::hit($key);
// Validate input
$this->validate();
try {
if ($this->isEditing) {
// Check if user can edit this review
if (!$this->review || $this->review->user_id !== Auth::id()) {
$this->message = 'You cannot edit this review.';
return;
}
// Update existing review
$this->review->update([
'rating' => $this->rating,
'title' => $this->title,
'content' => $this->content,
'status' => ReviewStatus::PENDING,
]);
$this->message = 'Review updated successfully. It will be visible after moderation.';
} else {
// Check if user already reviewed this ride
if (!$this->ride->canBeReviewedBy(Auth::user())) {
$this->message = 'You have already reviewed this ride.';
return;
}
// Create new review
Review::create([
'ride_id' => $this->ride->id,
'user_id' => Auth::id(),
'rating' => $this->rating,
'title' => $this->title,
'content' => $this->content,
'status' => ReviewStatus::PENDING,
]);
$this->message = 'Review submitted successfully. It will be visible after moderation.';
// Reset form
$this->reset(['rating', 'title', 'content']);
}
$this->dispatch('review-saved');
} catch (\Exception $e) {
$this->message = 'An error occurred while saving your review. Please try again.';
}
}
/**
* Reset the form
*/
public function resetForm()
{
$this->reset(['rating', 'title', 'content', 'message']);
if ($this->review) {
$this->rating = $this->review->rating;
$this->title = $this->review->title;
$this->content = $this->review->content;
}
}
/**
* Render the component
*/
public function render()
{
return view('livewire.ride-review-component');
}
}