# Operators Listing Page Implementation Prompt ## Django Parity Reference **Django Implementation**: `companies/views.py` - `CompanyListView` & `ManufacturerListView` (lines 62-126) **Django Template**: `companies/templates/companies/company_list.html` **Django Features**: Dual-role filtering (park operators vs ride manufacturers), industry statistics, portfolio showcases, corporate hierarchy display, market analysis ## Core Implementation Requirements ### Laravel/Livewire Architecture Generate the operators listing system using ThrillWiki's custom generators: ```bash # Generate unified operators listing with dual-role support php artisan make:thrillwiki-livewire OperatorsListing --paginated --cached --with-tests # Generate role-specific filtering component php artisan make:thrillwiki-livewire OperatorsRoleFilter --reusable --with-tests # Generate portfolio showcase component php artisan make:thrillwiki-livewire OperatorPortfolioCard --reusable --with-tests # Generate industry statistics dashboard php artisan make:thrillwiki-livewire OperatorsIndustryStats --reusable --cached # Generate corporate hierarchy visualization php artisan make:thrillwiki-livewire OperatorHierarchyView --reusable --with-tests # Generate market analysis component php artisan make:thrillwiki-livewire OperatorsMarketAnalysis --reusable --cached ``` ### Django Parity Features #### 1. Dual-Role Search Functionality **Django Implementation**: Multi-role search across: - Operator name (`name__icontains`) - Company description (`description__icontains`) - Founded year range (`founded_year__range`) - Headquarters location (`headquarters__city__icontains`) - Role-specific filtering (park_operator, ride_manufacturer, or both) - Industry sector (`industry_sector__icontains`) **Laravel Implementation**: ```php public function dualRoleSearch($query, $roles = []) { return Operator::query() ->when($query, function ($q) use ($query) { $terms = explode(' ', $query); foreach ($terms as $term) { $q->where(function ($subQuery) use ($term) { $subQuery->where('name', 'ilike', "%{$term}%") ->orWhere('description', 'ilike', "%{$term}%") ->orWhere('industry_sector', 'ilike', "%{$term}%") ->orWhereHas('location', function($locQuery) use ($term) { $locQuery->where('city', 'ilike', "%{$term}%") ->orWhere('state', 'ilike', "%{$term}%") ->orWhere('country', 'ilike', "%{$term}%"); }); }); } }) ->when($roles, function ($q) use ($roles) { $q->where(function ($roleQuery) use ($roles) { if (in_array('park_operator', $roles)) { $roleQuery->whereExists(function ($exists) { $exists->select(DB::raw(1)) ->from('parks') ->whereRaw('parks.operator_id = operators.id'); }); } if (in_array('ride_manufacturer', $roles)) { $roleQuery->orWhereExists(function ($exists) { $exists->select(DB::raw(1)) ->from('rides') ->whereRaw('rides.manufacturer_id = operators.id'); }); } if (in_array('ride_designer', $roles)) { $roleQuery->orWhereExists(function ($exists) { $exists->select(DB::raw(1)) ->from('rides') ->whereRaw('rides.designer_id = operators.id'); }); } }); }) ->with(['location', 'parks', 'manufactured_rides', 'designed_rides']) ->withCount(['parks', 'manufactured_rides', 'designed_rides']); } ``` #### 2. Advanced Industry Filtering **Django Filters**: - Role type (park_operator, manufacturer, designer, mixed) - Industry sector (entertainment, manufacturing, technology) - Company size (small, medium, large, enterprise) - Founded year range - Geographic presence (regional, national, international) - Market capitalization range - Annual revenue range **Laravel Filters Implementation**: ```php public function applyIndustryFilters($query, $filters) { return $query ->when($filters['role_type'] ?? null, function ($q, $roleType) { switch ($roleType) { case 'park_operator_only': $q->whereHas('parks')->whereDoesntHave('manufactured_rides'); break; case 'manufacturer_only': $q->whereHas('manufactured_rides')->whereDoesntHave('parks'); break; case 'mixed': $q->whereHas('parks')->whereHas('manufactured_rides'); break; case 'designer': $q->whereHas('designed_rides'); break; } }) ->when($filters['industry_sector'] ?? null, fn($q, $sector) => $q->where('industry_sector', $sector)) ->when($filters['company_size'] ?? null, function ($q, $size) { $ranges = [ 'small' => [1, 100], 'medium' => [101, 1000], 'large' => [1001, 10000], 'enterprise' => [10001, PHP_INT_MAX] ]; if (isset($ranges[$size])) { $q->whereBetween('employee_count', $ranges[$size]); } }) ->when($filters['founded_year_from'] ?? null, fn($q, $year) => $q->where('founded_year', '>=', $year)) ->when($filters['founded_year_to'] ?? null, fn($q, $year) => $q->where('founded_year', '<=', $year)) ->when($filters['geographic_presence'] ?? null, function ($q, $presence) { switch ($presence) { case 'regional': $q->whereHas('parks', function ($parkQ) { $parkQ->whereHas('location', function ($locQ) { $locQ->havingRaw('COUNT(DISTINCT country) = 1'); }); }); break; case 'international': $q->whereHas('parks', function ($parkQ) { $parkQ->whereHas('location', function ($locQ) { $locQ->havingRaw('COUNT(DISTINCT country) > 1'); }); }); break; } }) ->when($filters['min_revenue'] ?? null, fn($q, $revenue) => $q->where('annual_revenue', '>=', $revenue)) ->when($filters['max_revenue'] ?? null, fn($q, $revenue) => $q->where('annual_revenue', '<=', $revenue)); } ``` #### 3. Portfolio and Statistics Display **Portfolio Metrics**: - Total parks operated - Total rides manufactured/designed - Geographic reach (countries, continents) - Market share analysis - Revenue and financial metrics - Industry influence score ### Screen-Agnostic Design Implementation #### Mobile Layout (320px - 767px) - **Corporate Cards**: Compact operator cards with key metrics - **Role Badges**: Visual indicators for operator/manufacturer/designer roles - **Portfolio Highlights**: Key statistics prominently displayed - **Industry Filters**: Simplified filtering for mobile users **Mobile Component Structure**: ```blade
@foreach($operators as $operator) @endforeach
{{ $operators->links('pagination.mobile') }}
``` #### Tablet Layout (768px - 1023px) - **Dual-Pane Layout**: Filter sidebar + operator grid - **Portfolio Showcases**: Detailed portfolio cards for each operator - **Industry Dashboard**: Real-time industry statistics and trends - **Comparison Mode**: Side-by-side operator comparisons **Tablet Component Structure**: ```blade

{{ $operators->total() }} Industry Leaders

@if($view === 'grid')
@foreach($operators as $operator) @endforeach
@elseif($view === 'portfolio')
@foreach($operators as $operator) @endforeach
@else @endif
{{ $operators->links() }}
``` #### Desktop Layout (1024px - 1919px) - **Three-Pane Layout**: Filters + main content + industry insights - **Advanced Analytics**: Market share analysis and industry trends - **Corporate Hierarchies**: Visual representation of corporate structures - **Portfolio Deep Dives**: Comprehensive portfolio analysis **Desktop Component Structure**: ```blade

{{ $operators->total() }} Industry Operators

@if($view === 'grid')
@foreach($operators as $operator) @endforeach
{{ $operators->links('pagination.desktop') }}
@elseif($view === 'portfolio')
@foreach($operators as $operator) @endforeach
@elseif($view === 'hierarchy')
@else
@endif
``` #### Large Screen Layout (1920px+) - **Dashboard-Style Interface**: Comprehensive industry analytics - **Multi-Panel Views**: Simultaneous portfolio and market analysis - **Advanced Visualizations**: Corporate network maps and market dynamics - **Real-Time Market Data**: Live industry statistics and trends ### Performance Optimization Strategy #### Industry-Specific Caching ```php public function mount() { $this->industryStats = Cache::remember( 'operators.industry.stats', now()->addHours(6), fn() => $this->calculateIndustryStatistics() ); $this->marketData = Cache::remember( 'operators.market.data', now()->addHours(12), fn() => $this->loadMarketAnalysis() ); } public function getOperatorsProperty() { $cacheKey = "operators.listing." . md5(serialize([ 'search' => $this->search, 'filters' => $this->filters, 'role_filter' => $this->roleFilter, 'sort' => $this->sort, 'page' => $this->page ])); return Cache::remember($cacheKey, now()->addMinutes(30), function() { return $this->dualRoleSearch($this->search, $this->roleFilter) ->applyIndustryFilters($this->filters) ->orderBy($this->sort['column'], $this->sort['direction']) ->paginate(20); }); } ``` #### Financial Data Optimization ```php // Optimized query for financial and portfolio data public function optimizedFinancialQuery() { return Operator::select([ 'operators.*', DB::raw('COALESCE(parks_count.count, 0) as parks_count'), DB::raw('COALESCE(rides_count.count, 0) as manufactured_rides_count'), DB::raw('COALESCE(designed_rides_count.count, 0) as designed_rides_count'), DB::raw('CASE WHEN annual_revenue > 10000000000 THEN "enterprise" WHEN annual_revenue > 1000000000 THEN "large" WHEN annual_revenue > 100000000 THEN "medium" ELSE "small" END as company_size_category') ]) ->leftJoin(DB::raw('(SELECT operator_id, COUNT(*) as count FROM parks GROUP BY operator_id) as parks_count'), 'operators.id', '=', 'parks_count.operator_id') ->leftJoin(DB::raw('(SELECT manufacturer_id, COUNT(*) as count FROM rides GROUP BY manufacturer_id) as rides_count'), 'operators.id', '=', 'rides_count.manufacturer_id') ->leftJoin(DB::raw('(SELECT designer_id, COUNT(*) as count FROM rides GROUP BY designer_id) as designed_rides_count'), 'operators.id', '=', 'designed_rides_count.designer_id') ->with([ 'location:id,city,state,country', 'parks:id,operator_id,name,opening_date', 'manufactured_rides:id,manufacturer_id,name,ride_type', 'designed_rides:id,designer_id,name,ride_type' ]); } ``` ### Component Reuse Strategy #### Shared Components - **`OperatorsRoleFilter`**: Multi-role filtering with statistics - **`OperatorPortfolioCard`**: Comprehensive portfolio display - **`OperatorsIndustryStats`**: Real-time industry analytics - **`OperatorFinancialMetrics`**: Financial performance indicators #### Context Variations - **`ParkOperatorsListing`**: Park operators only with park portfolios - **`ManufacturersListing`**: Ride manufacturers with product catalogs - **`DesignersListing`**: Ride designers with design portfolios - **`CorporateGroupsListing`**: Corporate hierarchies and subsidiaries ### Testing Requirements #### Feature Tests ```php /** @test */ public function can_filter_operators_by_dual_roles() { $pureOperator = Operator::factory()->create(['name' => 'Disney Parks']); $pureOperator->parks()->create(['name' => 'Magic Kingdom']); $pureManufacturer = Operator::factory()->create(['name' => 'Intamin']); $pureManufacturer->manufactured_rides()->create(['name' => 'Millennium Force']); $mixedOperator = Operator::factory()->create(['name' => 'Universal']); $mixedOperator->parks()->create(['name' => 'Universal Studios']); $mixedOperator->manufactured_rides()->create(['name' => 'Custom Ride']); Livewire::test(OperatorsListing::class) ->set('roleFilter', ['park_operator']) ->assertSee($pureOperator->name) ->assertSee($mixedOperator->name) ->assertDontSee($pureManufacturer->name); } /** @test */ public function calculates_industry_statistics_correctly() { Operator::factory()->count(10)->create(['industry_sector' => 'entertainment']); Operator::factory()->count(5)->create(['industry_sector' => 'manufacturing']); $component = Livewire::test(OperatorsListing::class); $stats = $component->get('industryStats'); $this->assertEquals(15, $stats['total_operators']); $this->assertEquals(10, $stats['entertainment_operators']); $this->assertEquals(5, $stats['manufacturing_operators']); } /** @test */ public function maintains_django_parity_performance_with_portfolio_data() { Operator::factory()->count(50)->create(); $start = microtime(true); Livewire::test(OperatorsListing::class); $end = microtime(true); $this->assertLessThan(0.5, $end - $start); // < 500ms with portfolio data } ``` #### Financial Data Tests ```php /** @test */ public function categorizes_company_size_correctly() { $enterprise = Operator::factory()->create(['annual_revenue' => 15000000000]); $large = Operator::factory()->create(['annual_revenue' => 5000000000]); $medium = Operator::factory()->create(['annual_revenue' => 500000000]); $small = Operator::factory()->create(['annual_revenue' => 50000000]); Livewire::test(OperatorsListing::class) ->set('filters.company_size', 'enterprise') ->assertSee($enterprise->name) ->assertDontSee($large->name); } /** @test */ public function handles_portfolio_metrics_calculation() { $operator = Operator::factory()->create(); $operator->parks()->createMany(3, ['name' => 'Test Park']); $operator->manufactured_rides()->createMany(5, ['name' => 'Test Ride']); $component = Livewire::test(OperatorsListing::class); $portfolioData = $component->get('operators')->first(); $this->assertEquals(3, $portfolioData->parks_count); $this->assertEquals(5, $portfolioData->manufactured_rides_count); } ``` ### Performance Targets #### Universal Performance Standards with Financial Data - **Initial Load**: < 500ms (including industry statistics) - **Portfolio Calculation**: < 200ms for 100 operators - **Financial Filtering**: < 150ms with complex criteria - **Market Analysis**: < 1 second for trend calculations - **Industry Statistics**: < 100ms (cached) #### Industry-Specific Caching Strategy - **Market Data Cache**: 12 hours (financial markets change) - **Industry Statistics**: 6 hours (relatively stable) - **Portfolio Metrics**: 1 hour (operational data) - **Company Profiles**: 24 hours (corporate data stable) ### Success Criteria Checklist #### Django Parity Verification - [ ] Dual-role filtering matches Django behavior exactly - [ ] Industry statistics calculated identically to Django - [ ] Portfolio metrics match Django calculations - [ ] Financial filtering provides same results as Django - [ ] Corporate hierarchy display matches Django structure #### Screen-Agnostic Compliance - [ ] Mobile layout optimized for corporate data consumption - [ ] Tablet layout provides effective portfolio comparisons - [ ] Desktop layout maximizes industry analytics - [ ] Large screen layout provides comprehensive market view - [ ] All layouts handle complex financial data gracefully #### Performance Benchmarks - [ ] Initial load under 500ms including portfolio data - [ ] Financial calculations under 200ms - [ ] Industry statistics under 100ms (cached) - [ ] Market analysis under 1 second - [ ] Portfolio caching reduces server load by 60% #### Industry Feature Completeness - [ ] Dual-role filtering works across all operator types - [ ] Financial metrics display accurately - [ ] Portfolio showcases provide comprehensive overviews - [ ] Market analysis provides meaningful insights - [ ] Corporate hierarchies visualize relationships correctly This prompt ensures complete Django parity while providing comprehensive industry analysis capabilities that leverage modern data visualization and maintain ThrillWiki's screen-agnostic design principles.