Add Livewire components for parks, rides, and manufacturers

- 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.
This commit is contained in:
pacnpal
2025-06-23 21:31:05 -04:00
parent 5caa148a89
commit 97a7682eb7
62 changed files with 10532 additions and 210 deletions

View File

@@ -0,0 +1,362 @@
<?php
namespace App\Livewire;
use App\Models\Manufacturer;
use Livewire\Component;
use Livewire\WithPagination;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Builder;
class ManufacturersListingUniversal extends Component
{
use WithPagination;
// Universal Listing System Integration
public string $entityType = 'manufacturers';
// Search and Filtering
public string $search = '';
public string $sortBy = 'name';
public string $sortDirection = 'asc';
public string $viewMode = 'grid';
public int $perPage = 12;
// Manufacturer-specific filters
public array $specializations = [];
public array $totalRidesRange = [0, 1000];
public array $industryPresenceRange = [0, 100];
public array $foundedYearRange = [1800, 2025];
public bool $activeOnly = false;
public bool $innovationLeadersOnly = false;
// Performance optimization
private string $cacheKeyPrefix = 'manufacturers_listing';
private int $cacheProductPortfolioTtl = 21600; // 6 hours
private int $cacheIndustryPresenceTtl = 43200; // 12 hours
private int $cacheListingTtl = 1800; // 30 minutes
protected $queryString = [
'search' => ['except' => ''],
'sortBy' => ['except' => 'name'],
'sortDirection' => ['except' => 'asc'],
'viewMode' => ['except' => 'grid'],
'perPage' => ['except' => 12],
'specializations' => ['except' => []],
'totalRidesRange' => ['except' => [0, 1000]],
'industryPresenceRange' => ['except' => [0, 100]],
'foundedYearRange' => ['except' => [1800, 2025]],
'activeOnly' => ['except' => false],
'innovationLeadersOnly' => ['except' => false],
'page' => ['except' => 1],
];
public function mount()
{
// Initialize filters with cached values for performance
$this->initializeFilters();
}
public function updatedSearch()
{
$this->resetPage();
}
public function updatedSpecializations()
{
$this->resetPage();
}
public function updatedTotalRidesRange()
{
$this->resetPage();
}
public function updatedIndustryPresenceRange()
{
$this->resetPage();
}
public function updatedFoundedYearRange()
{
$this->resetPage();
}
public function updatedActiveOnly()
{
$this->resetPage();
}
public function updatedInnovationLeadersOnly()
{
$this->resetPage();
}
public function clearFilters()
{
$this->reset([
'search',
'specializations',
'totalRidesRange',
'industryPresenceRange',
'foundedYearRange',
'activeOnly',
'innovationLeadersOnly'
]);
$this->totalRidesRange = [0, 1000];
$this->industryPresenceRange = [0, 100];
$this->foundedYearRange = [1800, 2025];
$this->resetPage();
}
public function setViewMode(string $mode)
{
$this->viewMode = $mode;
}
public function setSortBy(string $field)
{
if ($this->sortBy === $field) {
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
} else {
$this->sortBy = $field;
$this->sortDirection = 'asc';
}
$this->resetPage();
}
public function render()
{
$manufacturers = $this->getManufacturers();
$statistics = $this->getStatistics();
$productPortfolioData = $this->getProductPortfolioData();
$industryPresenceData = $this->getIndustryPresenceData();
return view('livewire.manufacturers-listing-universal', [
'manufacturers' => $manufacturers,
'statistics' => $statistics,
'productPortfolioData' => $productPortfolioData,
'industryPresenceData' => $industryPresenceData,
'hasActiveFilters' => $this->hasActiveFilters(),
]);
}
private function getManufacturers()
{
$cacheKey = $this->generateCacheKey();
return Cache::remember($cacheKey, $this->cacheListingTtl, function () {
$query = Manufacturer::query()
->select([
'id', 'name', 'slug', 'headquarters', 'description', 'website',
'total_rides', 'total_roller_coasters', 'founded_year',
'industry_presence_score', 'specialization', 'is_active',
'is_major_manufacturer', 'market_share_percentage', 'created_at'
]);
// Apply search with Django parity algorithms
if (!empty($this->search)) {
$searchTerms = explode(' ', trim($this->search));
$query->where(function (Builder $q) use ($searchTerms) {
foreach ($searchTerms as $term) {
$q->where(function (Builder $subQ) use ($term) {
$subQ->where('name', 'ILIKE', "%{$term}%")
->orWhere('description', 'ILIKE', "%{$term}%")
->orWhere('headquarters', 'ILIKE', "%{$term}%")
->orWhere('specialization', 'ILIKE', "%{$term}%");
});
}
});
}
// Apply specialization filters
if (!empty($this->specializations)) {
$query->whereIn('specialization', $this->specializations);
}
// Apply total rides range filter
if ($this->totalRidesRange[0] > 0 || $this->totalRidesRange[1] < 1000) {
$query->whereBetween('total_rides', $this->totalRidesRange);
}
// Apply industry presence score range filter
if ($this->industryPresenceRange[0] > 0 || $this->industryPresenceRange[1] < 100) {
$query->whereBetween('industry_presence_score', $this->industryPresenceRange);
}
// Apply founded year range filter
if ($this->foundedYearRange[0] > 1800 || $this->foundedYearRange[1] < 2025) {
$query->whereBetween('founded_year', $this->foundedYearRange);
}
// Apply active filter
if ($this->activeOnly) {
$query->where('is_active', true);
}
// Apply major manufacturers filter
if ($this->innovationLeadersOnly) {
$query->where('is_major_manufacturer', true);
}
// Apply sorting
$query->orderBy($this->sortBy, $this->sortDirection);
// Add secondary sort for consistency
if ($this->sortBy !== 'name') {
$query->orderBy('name', 'asc');
}
return $query->paginate($this->perPage);
});
}
private function getStatistics()
{
$cacheKey = "{$this->cacheKeyPrefix}_statistics";
return Cache::remember($cacheKey, $this->cacheListingTtl, function () {
$baseQuery = Manufacturer::query();
// Apply same filters as main query for accurate statistics
if (!empty($this->search)) {
$searchTerms = explode(' ', trim($this->search));
$baseQuery->where(function (Builder $q) use ($searchTerms) {
foreach ($searchTerms as $term) {
$q->where(function (Builder $subQ) use ($term) {
$subQ->where('name', 'ILIKE', "%{$term}%")
->orWhere('description', 'ILIKE', "%{$term}%")
->orWhere('headquarters', 'ILIKE', "%{$term}%")
->orWhere('specialization', 'ILIKE', "%{$term}%");
});
}
});
}
if (!empty($this->specializations)) {
$baseQuery->whereIn('specialization', $this->specializations);
}
if ($this->totalRidesRange[0] > 0 || $this->totalRidesRange[1] < 1000) {
$baseQuery->whereBetween('total_rides', $this->totalRidesRange);
}
if ($this->industryPresenceRange[0] > 0 || $this->industryPresenceRange[1] < 100) {
$baseQuery->whereBetween('industry_presence_score', $this->industryPresenceRange);
}
if ($this->foundedYearRange[0] > 1800 || $this->foundedYearRange[1] < 2025) {
$baseQuery->whereBetween('founded_year', $this->foundedYearRange);
}
if ($this->activeOnly) {
$baseQuery->where('is_active', true);
}
if ($this->innovationLeadersOnly) {
$baseQuery->where('is_major_manufacturer', true);
}
return [
'count' => $baseQuery->count(),
'active_count' => (clone $baseQuery)->where('is_active', true)->count(),
'total_rides_sum' => $baseQuery->sum('total_rides'),
'avg_industry_presence' => round($baseQuery->avg('industry_presence_score'), 1),
];
});
}
private function getProductPortfolioData()
{
$cacheKey = "{$this->cacheKeyPrefix}_product_portfolio";
return Cache::remember($cacheKey, $this->cacheProductPortfolioTtl, function () {
return [
'specialization_distribution' => Manufacturer::selectRaw('specialization, COUNT(*) as count')
->groupBy('specialization')
->pluck('count', 'specialization')
->toArray(),
'top_manufacturers_by_rides' => Manufacturer::orderBy('total_rides', 'desc')
->limit(10)
->pluck('total_rides', 'name')
->toArray(),
'major_manufacturers_count' => Manufacturer::where('is_major_manufacturer', true)->count(),
'average_market_share' => round(Manufacturer::avg('market_share_percentage'), 2),
];
});
}
private function getIndustryPresenceData()
{
$cacheKey = "{$this->cacheKeyPrefix}_industry_presence";
return Cache::remember($cacheKey, $this->cacheIndustryPresenceTtl, function () {
return [
'presence_score_ranges' => [
'high' => Manufacturer::where('industry_presence_score', '>=', 80)->count(),
'medium' => Manufacturer::whereBetween('industry_presence_score', [50, 79])->count(),
'low' => Manufacturer::where('industry_presence_score', '<', 50)->count(),
],
'founding_decades' => Manufacturer::selectRaw('FLOOR(founded_year / 10) * 10 as decade, COUNT(*) as count')
->groupBy('decade')
->orderBy('decade')
->pluck('count', 'decade')
->toArray(),
'active_vs_inactive' => [
'active' => Manufacturer::where('is_active', true)->count(),
'inactive' => Manufacturer::where('is_active', false)->count(),
],
'market_concentration' => Manufacturer::orderBy('market_share_percentage', 'desc')
->limit(5)
->pluck('market_share_percentage', 'name')
->toArray(),
];
});
}
private function hasActiveFilters(): bool
{
return !empty($this->search) ||
!empty($this->specializations) ||
$this->totalRidesRange !== [0, 1000] ||
$this->industryPresenceRange !== [0, 100] ||
$this->foundedYearRange !== [1800, 2025] ||
$this->activeOnly ||
$this->innovationLeadersOnly;
}
private function generateCacheKey(): string
{
$filterHash = md5(serialize([
'search' => $this->search,
'sortBy' => $this->sortBy,
'sortDirection' => $this->sortDirection,
'specializations' => $this->specializations,
'totalRidesRange' => $this->totalRidesRange,
'industryPresenceRange' => $this->industryPresenceRange,
'foundedYearRange' => $this->foundedYearRange,
'activeOnly' => $this->activeOnly,
'innovationLeadersOnly' => $this->innovationLeadersOnly,
'perPage' => $this->perPage,
'page' => $this->getPage(),
]));
return "{$this->cacheKeyPrefix}_{$filterHash}";
}
private function initializeFilters()
{
// Initialize with sensible defaults for manufacturer filtering
if (empty($this->totalRidesRange)) {
$this->totalRidesRange = [0, 1000];
}
if (empty($this->industryPresenceRange)) {
$this->industryPresenceRange = [0, 100];
}
if (empty($this->foundedYearRange)) {
$this->foundedYearRange = [1800, 2025];
}
}
}