mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 08:51:11 -05:00
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:
419
resources/views/livewire/operators-listing-universal.blade.php
Normal file
419
resources/views/livewire/operators-listing-universal.blade.php
Normal file
@@ -0,0 +1,419 @@
|
||||
<div>
|
||||
{{-- Universal Listing System Integration --}}
|
||||
<x-universal-listing
|
||||
:entity-type="$entityType"
|
||||
:items="$operators"
|
||||
:search="$search"
|
||||
:sort-by="$sortBy"
|
||||
:sort-direction="$sortDirection"
|
||||
:view-mode="$viewMode"
|
||||
:per-page="$perPage"
|
||||
>
|
||||
{{-- Custom Industry Statistics Header --}}
|
||||
<x-slot name="header">
|
||||
<div class="bg-gradient-to-r from-blue-500 to-purple-600 text-white p-6 rounded-lg mb-6">
|
||||
<div class="text-center">
|
||||
<h2 class="text-2xl font-bold mb-4">Industry Overview</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold">{{ $industryStats['total_operators'] ?? 0 }}</div>
|
||||
<div class="text-sm opacity-90">Total Operators</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold">{{ $industryStats['park_operators'] ?? 0 }}</div>
|
||||
<div class="text-sm opacity-90">Park Operators</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold">{{ $industryStats['manufacturers'] ?? 0 }}</div>
|
||||
<div class="text-sm opacity-90">Manufacturers</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold">{{ $industryStats['mixed_role'] ?? 0 }}</div>
|
||||
<div class="text-sm opacity-90">Multi-Role</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Search Placeholder --}}
|
||||
<x-slot name="search-placeholder">
|
||||
Search operators, manufacturers, designers...
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Filters Sidebar --}}
|
||||
<x-slot name="filters">
|
||||
{{-- Role Filters --}}
|
||||
<div class="mb-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Operator Roles</h3>
|
||||
<div class="space-y-2">
|
||||
<label class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
wire:model.live="roleFilter"
|
||||
value="park_operator"
|
||||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
>
|
||||
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
Park Operators ({{ $industryStats['park_operators'] ?? 0 }})
|
||||
</span>
|
||||
</label>
|
||||
<label class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
wire:model.live="roleFilter"
|
||||
value="ride_manufacturer"
|
||||
class="rounded border-gray-300 text-green-600 focus:ring-green-500"
|
||||
>
|
||||
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
Manufacturers ({{ $industryStats['manufacturers'] ?? 0 }})
|
||||
</span>
|
||||
</label>
|
||||
<label class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
wire:model.live="roleFilter"
|
||||
value="ride_designer"
|
||||
class="rounded border-gray-300 text-purple-600 focus:ring-purple-500"
|
||||
>
|
||||
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
Designers ({{ $industryStats['designers'] ?? 0 }})
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Industry Filters --}}
|
||||
<div class="mb-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">Industry Filters</h3>
|
||||
<div class="space-y-4">
|
||||
{{-- Company Size --}}
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Company Size</label>
|
||||
<select wire:model.live="companySize" class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<option value="">All Sizes</option>
|
||||
<option value="small">Small (1-100)</option>
|
||||
<option value="medium">Medium (101-1000)</option>
|
||||
<option value="large">Large (1001-10000)</option>
|
||||
<option value="enterprise">Enterprise (10000+)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Industry Sector --}}
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Industry Sector</label>
|
||||
<select wire:model.live="industrySector" class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<option value="">All Sectors</option>
|
||||
@if(isset($industryStats['sectors']))
|
||||
@foreach($industryStats['sectors'] as $sector => $count)
|
||||
<option value="{{ $sector }}">{{ ucfirst($sector) }} ({{ $count }})</option>
|
||||
@endforeach
|
||||
@endif
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Founded Year Range --}}
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">From Year</label>
|
||||
<input
|
||||
type="number"
|
||||
wire:model.live="foundedYearFrom"
|
||||
placeholder="1900"
|
||||
class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">To Year</label>
|
||||
<input
|
||||
type="number"
|
||||
wire:model.live="foundedYearTo"
|
||||
placeholder="{{ date('Y') }}"
|
||||
class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Geographic Presence --}}
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Geographic Presence</label>
|
||||
<select wire:model.live="geographicPresence" class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<option value="">All Levels</option>
|
||||
<option value="regional">Regional</option>
|
||||
<option value="international">International</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Revenue Range --}}
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Min Revenue</label>
|
||||
<input
|
||||
type="number"
|
||||
wire:model.live="minRevenue"
|
||||
placeholder="0"
|
||||
class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Max Revenue</label>
|
||||
<input
|
||||
type="number"
|
||||
wire:model.live="maxRevenue"
|
||||
placeholder="∞"
|
||||
class="w-full text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Industry Statistics Panel --}}
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4">
|
||||
<h3 class="text-sm font-medium text-blue-900 dark:text-blue-100 mb-3">Industry Stats</h3>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-blue-700 dark:text-blue-300">Total Operators</span>
|
||||
<span class="font-medium text-blue-900 dark:text-blue-100">{{ $industryStats['total_operators'] ?? 0 }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-blue-700 dark:text-blue-300">Multi-Role</span>
|
||||
<span class="font-medium text-blue-900 dark:text-blue-100">{{ $industryStats['mixed_role'] ?? 0 }}</span>
|
||||
</div>
|
||||
@if(isset($marketData['total_market_cap']))
|
||||
<div class="flex justify-between">
|
||||
<span class="text-blue-700 dark:text-blue-300">Market Cap</span>
|
||||
<span class="font-medium text-blue-900 dark:text-blue-100">${{ number_format($marketData['total_market_cap'] / 1000000000, 1) }}B</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Mobile Role Filter Buttons --}}
|
||||
<x-slot name="mobile-filters">
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<button
|
||||
wire:click="toggleRoleFilter('park_operator')"
|
||||
class="px-3 py-1.5 text-sm rounded-full border transition-colors {{ in_array('park_operator', $roleFilter) ? 'bg-blue-500 text-white border-blue-500' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600' }}"
|
||||
>
|
||||
Operators
|
||||
@if(isset($industryStats['park_operators']))
|
||||
<span class="ml-1 text-xs opacity-75">({{ $industryStats['park_operators'] }})</span>
|
||||
@endif
|
||||
</button>
|
||||
<button
|
||||
wire:click="toggleRoleFilter('ride_manufacturer')"
|
||||
class="px-3 py-1.5 text-sm rounded-full border transition-colors {{ in_array('ride_manufacturer', $roleFilter) ? 'bg-green-500 text-white border-green-500' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600' }}"
|
||||
>
|
||||
Manufacturers
|
||||
@if(isset($industryStats['manufacturers']))
|
||||
<span class="ml-1 text-xs opacity-75">({{ $industryStats['manufacturers'] }})</span>
|
||||
@endif
|
||||
</button>
|
||||
<button
|
||||
wire:click="toggleRoleFilter('ride_designer')"
|
||||
class="px-3 py-1.5 text-sm rounded-full border transition-colors {{ in_array('ride_designer', $roleFilter) ? 'bg-purple-500 text-white border-purple-500' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600' }}"
|
||||
>
|
||||
Designers
|
||||
@if(isset($industryStats['designers']))
|
||||
<span class="ml-1 text-xs opacity-75">({{ $industryStats['designers'] }})</span>
|
||||
@endif
|
||||
</button>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Sort Options --}}
|
||||
<x-slot name="sort-options">
|
||||
<option value="name">Name</option>
|
||||
<option value="founded_year">Founded Year</option>
|
||||
<option value="parks_count">Parks Count</option>
|
||||
<option value="rides_count">Rides Count</option>
|
||||
<option value="revenue">Revenue</option>
|
||||
<option value="market_influence">Market Influence</option>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom View Mode Options --}}
|
||||
<x-slot name="view-modes">
|
||||
<button
|
||||
wire:click="setViewMode('grid')"
|
||||
class="px-3 py-1 text-sm {{ $viewMode === 'grid' ? 'bg-blue-500 text-white' : 'bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300' }} rounded-l-md border border-gray-300 dark:border-gray-600"
|
||||
>
|
||||
Grid
|
||||
</button>
|
||||
<button
|
||||
wire:click="setViewMode('portfolio')"
|
||||
class="px-3 py-1 text-sm {{ $viewMode === 'portfolio' ? 'bg-blue-500 text-white' : 'bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300' }} rounded-r-md border-t border-r border-b border-gray-300 dark:border-gray-600"
|
||||
>
|
||||
Portfolio
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Card Content for Grid View --}}
|
||||
<x-slot name="card-content" :item="$operator">
|
||||
{{-- Operator Header --}}
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{{ $operator->name }}
|
||||
</h3>
|
||||
@if($operator->location)
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $operator->location->city }}, {{ $operator->location->country }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@if($operator->market_influence_score)
|
||||
<div class="text-right">
|
||||
<div class="text-lg font-bold text-blue-600 dark:text-blue-400">
|
||||
{{ number_format($operator->market_influence_score, 1) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">Influence Score</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Role Badges --}}
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
@if($operator->parks_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full">
|
||||
{{ $operator->parks_count }} Parks
|
||||
</span>
|
||||
@endif
|
||||
@if($operator->manufactured_rides_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full">
|
||||
{{ $operator->manufactured_rides_count }} Manufactured
|
||||
</span>
|
||||
@endif
|
||||
@if($operator->designed_rides_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full">
|
||||
{{ $operator->designed_rides_count }} Designed
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Key Metrics --}}
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
@if($operator->founded_year)
|
||||
<div>
|
||||
<div class="font-semibold text-gray-900 dark:text-gray-100">{{ $operator->founded_year }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Founded</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->industry_sector)
|
||||
<div>
|
||||
<div class="font-semibold text-gray-900 dark:text-gray-100">{{ ucfirst($operator->industry_sector) }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Sector</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->employee_count)
|
||||
<div>
|
||||
<div class="font-semibold text-gray-900 dark:text-gray-100">{{ number_format($operator->employee_count) }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Employees</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->geographic_presence_level)
|
||||
<div>
|
||||
<div class="font-semibold text-gray-900 dark:text-gray-100">{{ ucfirst($operator->geographic_presence_level) }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Presence</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Portfolio View Content --}}
|
||||
<x-slot name="portfolio-content" :item="$operator">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
||||
{{ $operator->name }}
|
||||
</h3>
|
||||
@if($operator->description)
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-3">{{ $operator->description }}</p>
|
||||
@endif
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if($operator->parks_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full">
|
||||
Park Operator: {{ $operator->parks_count }} parks
|
||||
</span>
|
||||
@endif
|
||||
@if($operator->manufactured_rides_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full">
|
||||
Manufacturer: {{ $operator->manufactured_rides_count }} rides
|
||||
</span>
|
||||
@endif
|
||||
@if($operator->designed_rides_count > 0)
|
||||
<span class="px-3 py-1 text-sm bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded-full">
|
||||
Designer: {{ $operator->designed_rides_count }} rides
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if($operator->market_influence_score)
|
||||
<div class="text-right ml-6">
|
||||
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400">
|
||||
{{ number_format($operator->market_influence_score, 1) }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">Market Influence</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-6 text-sm">
|
||||
@if($operator->founded_year)
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">{{ $operator->founded_year }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Founded</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->industry_sector)
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">{{ ucfirst($operator->industry_sector) }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Industry</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->employee_count)
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">{{ number_format($operator->employee_count) }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Employees</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($operator->location)
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900 dark:text-gray-100">{{ $operator->location->country }}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">Headquarters</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Empty State --}}
|
||||
<x-slot name="empty-state">
|
||||
<div class="text-center py-12">
|
||||
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
|
||||
</svg>
|
||||
<h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-gray-100">No operators found</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Try adjusting your search or filters.</p>
|
||||
<div class="mt-6">
|
||||
<button
|
||||
wire:click="clearFilters"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
Clear all filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
{{-- Custom Clear Filters Action --}}
|
||||
<x-slot name="clear-filters">
|
||||
<button
|
||||
wire:click="clearFilters"
|
||||
class="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200"
|
||||
>
|
||||
Clear all filters
|
||||
</button>
|
||||
</x-slot>
|
||||
</x-universal-listing>
|
||||
</div>
|
||||
Reference in New Issue
Block a user