mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 06:31:10 -05:00
- Implemented ParksLocationSearch component with loading state and refresh functionality. - Created ParksMapView component with similar structure and functionality. - Added RegionalParksListing component for displaying regional parks. - Developed RidesListingUniversal component for universal listing integration. - Established ManufacturersListing view with navigation and Livewire integration. - Added feature tests for various Livewire components including OperatorHierarchyView, OperatorParksListing, OperatorPortfolioCard, OperatorsListing, OperatorsRoleFilter, ParksListing, ParksLocationSearch, ParksMapView, and RegionalParksListing to ensure proper rendering and adherence to patterns.
372 lines
10 KiB
PHP
372 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Models\Ride;
|
|
use App\Models\Park;
|
|
use App\Enums\RideCategory;
|
|
use App\Enums\RideStatus;
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
use Livewire\Attributes\Url;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class ParkRidesListing extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
// Required park context
|
|
public Park $park;
|
|
|
|
// URL-bound search and filter properties
|
|
#[Url(as: 'search')]
|
|
public string $searchTerm = '';
|
|
|
|
#[Url(as: 'category')]
|
|
public ?string $selectedCategory = null;
|
|
|
|
#[Url(as: 'status')]
|
|
public ?string $selectedStatus = null;
|
|
|
|
#[Url(as: 'sort')]
|
|
public string $sortBy = 'name';
|
|
|
|
#[Url(as: 'direction')]
|
|
public string $sortDirection = 'asc';
|
|
|
|
// UI state
|
|
public bool $showFilters = false;
|
|
public int $perPage = 12;
|
|
|
|
// Cached data
|
|
public array $categories = [];
|
|
public array $statuses = [];
|
|
public array $sortOptions = [];
|
|
|
|
/**
|
|
* Component initialization
|
|
*/
|
|
public function mount(Park $park): void
|
|
{
|
|
$this->park = $park;
|
|
$this->loadFilterOptions();
|
|
$this->setupSortOptions();
|
|
}
|
|
|
|
/**
|
|
* Load filter options specific to this park
|
|
*/
|
|
protected function loadFilterOptions(): void
|
|
{
|
|
$cacheKey = "park_rides_filters_{$this->park->id}";
|
|
|
|
$filterData = Cache::remember($cacheKey, 3600, function() {
|
|
// Categories available in this park
|
|
$categories = $this->park->rides()
|
|
->select('category')
|
|
->groupBy('category')
|
|
->get()
|
|
->map(function($ride) {
|
|
$category = RideCategory::from($ride->category);
|
|
return [
|
|
'value' => $category->value,
|
|
'label' => $category->name,
|
|
'count' => $this->park->rides()->where('category', $category->value)->count()
|
|
];
|
|
})
|
|
->toArray();
|
|
|
|
// Statuses available in this park
|
|
$statuses = $this->park->rides()
|
|
->select('status')
|
|
->groupBy('status')
|
|
->get()
|
|
->map(function($ride) {
|
|
$status = RideStatus::from($ride->status);
|
|
return [
|
|
'value' => $status->value,
|
|
'label' => $status->name,
|
|
'count' => $this->park->rides()->where('status', $status->value)->count()
|
|
];
|
|
})
|
|
->toArray();
|
|
|
|
return compact('categories', 'statuses');
|
|
});
|
|
|
|
$this->categories = $filterData['categories'];
|
|
$this->statuses = $filterData['statuses'];
|
|
}
|
|
|
|
/**
|
|
* Setup sort options
|
|
*/
|
|
protected function setupSortOptions(): void
|
|
{
|
|
$this->sortOptions = [
|
|
'name' => 'Name',
|
|
'opening_year' => 'Opening Year',
|
|
'height_requirement' => 'Height Requirement',
|
|
'created_at' => 'Date Added',
|
|
'updated_at' => 'Last Updated'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Update search term and reset pagination
|
|
*/
|
|
public function updatedSearchTerm(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Update category filter
|
|
*/
|
|
public function updatedSelectedCategory(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Update status filter
|
|
*/
|
|
public function updatedSelectedStatus(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Update sort options
|
|
*/
|
|
public function updatedSortBy(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Update sort direction
|
|
*/
|
|
public function updatedSortDirection(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Set category filter
|
|
*/
|
|
public function setCategory(?string $category): void
|
|
{
|
|
$this->selectedCategory = $category === $this->selectedCategory ? null : $category;
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Set status filter
|
|
*/
|
|
public function setStatus(?string $status): void
|
|
{
|
|
$this->selectedStatus = $status === $this->selectedStatus ? null : $status;
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Set sort parameters
|
|
*/
|
|
public function setSortBy(string $field): void
|
|
{
|
|
if ($this->sortBy === $field) {
|
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
$this->sortBy = $field;
|
|
$this->sortDirection = 'asc';
|
|
}
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Toggle filters visibility
|
|
*/
|
|
public function toggleFilters(): void
|
|
{
|
|
$this->showFilters = !$this->showFilters;
|
|
}
|
|
|
|
/**
|
|
* Clear all filters
|
|
*/
|
|
public function clearFilters(): void
|
|
{
|
|
$this->searchTerm = '';
|
|
$this->selectedCategory = null;
|
|
$this->selectedStatus = null;
|
|
$this->sortBy = 'name';
|
|
$this->sortDirection = 'asc';
|
|
$this->resetPage();
|
|
}
|
|
|
|
/**
|
|
* Get filtered and sorted rides for this park
|
|
*/
|
|
public function getRidesProperty()
|
|
{
|
|
$cacheKey = $this->getCacheKey();
|
|
|
|
return Cache::remember($cacheKey, 300, function() {
|
|
$query = $this->park->rides()
|
|
->with(['manufacturer', 'designer', 'photos'])
|
|
->when($this->searchTerm, function (Builder $query) {
|
|
$query->where(function (Builder $subQuery) {
|
|
$subQuery->where('name', 'ILIKE', "%{$this->searchTerm}%")
|
|
->orWhere('description', 'ILIKE', "%{$this->searchTerm}%")
|
|
->orWhereHas('manufacturer', function (Builder $manufacturerQuery) {
|
|
$manufacturerQuery->where('name', 'ILIKE', "%{$this->searchTerm}%");
|
|
})
|
|
->orWhereHas('designer', function (Builder $designerQuery) {
|
|
$designerQuery->where('name', 'ILIKE', "%{$this->searchTerm}%");
|
|
});
|
|
});
|
|
})
|
|
->when($this->selectedCategory, function (Builder $query) {
|
|
$query->where('category', $this->selectedCategory);
|
|
})
|
|
->when($this->selectedStatus, function (Builder $query) {
|
|
$query->where('status', $this->selectedStatus);
|
|
});
|
|
|
|
// Apply sorting
|
|
switch ($this->sortBy) {
|
|
case 'name':
|
|
$query->orderBy('name', $this->sortDirection);
|
|
break;
|
|
case 'opening_year':
|
|
$query->orderBy('opening_year', $this->sortDirection)
|
|
->orderBy('name', 'asc');
|
|
break;
|
|
case 'height_requirement':
|
|
$query->orderBy('height_requirement', $this->sortDirection)
|
|
->orderBy('name', 'asc');
|
|
break;
|
|
case 'created_at':
|
|
$query->orderBy('created_at', $this->sortDirection);
|
|
break;
|
|
case 'updated_at':
|
|
$query->orderBy('updated_at', $this->sortDirection);
|
|
break;
|
|
default:
|
|
$query->orderBy('name', 'asc');
|
|
}
|
|
|
|
return $query->paginate($this->perPage);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get park statistics
|
|
*/
|
|
public function getParkStatsProperty(): array
|
|
{
|
|
$cacheKey = "park_stats_{$this->park->id}";
|
|
|
|
return Cache::remember($cacheKey, 3600, function() {
|
|
$totalRides = $this->park->rides()->count();
|
|
$operatingRides = $this->park->rides()->where('status', 'operating')->count();
|
|
$categories = $this->park->rides()
|
|
->select('category')
|
|
->groupBy('category')
|
|
->get()
|
|
->count();
|
|
|
|
$avgRating = $this->park->rides()
|
|
->whereHas('reviews')
|
|
->withAvg('reviews', 'rating')
|
|
->get()
|
|
->avg('reviews_avg_rating');
|
|
|
|
return [
|
|
'total_rides' => $totalRides,
|
|
'operating_rides' => $operatingRides,
|
|
'categories' => $categories,
|
|
'avg_rating' => $avgRating ? round($avgRating, 1) : null
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get active filters count
|
|
*/
|
|
public function getActiveFiltersCountProperty(): int
|
|
{
|
|
return collect([
|
|
$this->searchTerm,
|
|
$this->selectedCategory,
|
|
$this->selectedStatus
|
|
])->filter()->count();
|
|
}
|
|
|
|
/**
|
|
* Get cache key for current state
|
|
*/
|
|
protected function getCacheKey(): string
|
|
{
|
|
return sprintf(
|
|
'park_rides_%d_%s_%s_%s_%s_%s_%d_%d',
|
|
$this->park->id,
|
|
md5($this->searchTerm),
|
|
$this->selectedCategory ?? 'all',
|
|
$this->selectedStatus ?? 'all',
|
|
$this->sortBy,
|
|
$this->sortDirection,
|
|
$this->perPage,
|
|
$this->getPage()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render the component
|
|
*/
|
|
public function render()
|
|
{
|
|
return view('livewire.park-rides-listing', [
|
|
'rides' => $this->rides,
|
|
'parkStats' => $this->parkStats,
|
|
'activeFiltersCount' => $this->activeFiltersCount
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Reset pagination when filters change
|
|
*/
|
|
public function resetPage($pageName = 'page'): void
|
|
{
|
|
$this->resetPage($pageName);
|
|
|
|
// Clear cache when filters change
|
|
$this->clearComponentCache();
|
|
}
|
|
|
|
/**
|
|
* Clear component-specific cache
|
|
*/
|
|
protected function clearComponentCache(): void
|
|
{
|
|
$patterns = [
|
|
"park_rides_{$this->park->id}_*",
|
|
"park_stats_{$this->park->id}",
|
|
"park_rides_filters_{$this->park->id}"
|
|
];
|
|
|
|
foreach ($patterns as $pattern) {
|
|
Cache::forget($pattern);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get pagination view
|
|
*/
|
|
public function paginationView(): string
|
|
{
|
|
return 'livewire.pagination-links';
|
|
}
|
|
} |