# 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
<div class="operators-mobile-layout">
    <!-- Industry Search Bar -->
    <div class="sticky top-0 bg-white dark:bg-gray-900 z-20 p-4">
        <livewire:operators-industry-search />
        <div class="flex items-center mt-2 space-x-2">
            <button wire:click="filterByRole('park_operator')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeRole === 'park_operator' ? 'bg-blue-500 text-white' : 'bg-blue-100 dark:bg-blue-900' }} rounded-full">
                <span class="text-sm">Operators</span>
            </button>
            <button wire:click="filterByRole('manufacturer')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeRole === 'manufacturer' ? 'bg-green-500 text-white' : 'bg-green-100 dark:bg-green-900' }} rounded-full">
                <span class="text-sm">Manufacturers</span>
            </button>
        </div>
    </div>
    
    <!-- Industry Statistics Banner -->
    <div class="bg-gradient-to-r from-blue-500 to-purple-600 text-white p-4 m-4 rounded-lg">
        <livewire:operators-industry-stats :compact="true" />
    </div>
    
    <!-- Quick Filters -->
    <div class="horizontal-scroll p-4 pb-2">
        <livewire:operators-quick-filters />
    </div>
    
    <!-- Operator Cards -->
    <div class="space-y-4 p-4">
        @foreach($operators as $operator)
            <livewire:operator-mobile-card :operator="$operator" :show-portfolio="true" :key="$operator->id" />
        @endforeach
    </div>
    
    <!-- Mobile Pagination -->
    <div class="sticky bottom-0 bg-white dark:bg-gray-900 p-4">
        {{ $operators->links('pagination.mobile') }}
    </div>
</div>
```

#### 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
<div class="operators-tablet-layout flex h-screen">
    <!-- Industry Filter Sidebar -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:operators-industry-search :advanced="true" />
            <div class="mt-6">
                <livewire:operators-role-filter :expanded="true" />
            </div>
            <div class="mt-6">
                <livewire:operators-industry-filters :show-financial="true" />
            </div>
            <div class="mt-6">
                <livewire:operators-industry-stats :detailed="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content Area -->
    <div class="flex-1 flex flex-col">
        <!-- Industry Header -->
        <div class="bg-white dark:bg-gray-900 p-4 border-b border-gray-200 dark:border-gray-700">
            <div class="flex items-center justify-between">
                <div class="flex items-center space-x-4">
                    <h2 class="text-xl font-semibold">{{ $operators->total() }} Industry Leaders</h2>
                    <livewire:operators-market-overview />
                </div>
                <div class="flex items-center space-x-2">
                    <livewire:operators-sort-selector />
                    <livewire:operators-view-toggle />
                </div>
            </div>
        </div>
        
        <!-- Content Grid -->
        <div class="flex-1 overflow-y-auto p-6">
            @if($view === 'grid')
                <div class="grid grid-cols-2 gap-6">
                    @foreach($operators as $operator)
                        <livewire:operator-tablet-card :operator="$operator" :detailed="true" :key="$operator->id" />
                    @endforeach
                </div>
            @elseif($view === 'portfolio')
                <div class="space-y-6">
                    @foreach($operators as $operator)
                        <livewire:operator-portfolio-showcase :operator="$operator" :key="$operator->id" />
                    @endforeach
                </div>
            @else
                <livewire:operators-market-analysis :operators="$operators" />
            @endif
            
            <div class="mt-6">
                {{ $operators->links() }}
            </div>
        </div>
    </div>
</div>
```

#### 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
<div class="operators-desktop-layout flex h-screen">
    <!-- Advanced Filter Sidebar -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:operators-industry-search :advanced="true" :autocomplete="true" />
            <div class="mt-6">
                <livewire:operators-role-filter :advanced="true" :show-statistics="true" />
            </div>
            <div class="mt-6">
                <livewire:operators-industry-filters :advanced="true" :show-financial="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content -->
    <div class="flex-1 flex flex-col">
        <!-- Industry Dashboard Header -->
        <div class="bg-white dark:bg-gray-900 p-6 border-b border-gray-200 dark:border-gray-700">
            <div class="flex items-center justify-between mb-4">
                <div class="flex items-center space-x-6">
                    <h1 class="text-2xl font-bold">{{ $operators->total() }} Industry Operators</h1>
                    <livewire:operators-market-summary />
                </div>
                <div class="flex items-center space-x-4">
                    <livewire:operators-sort-selector :advanced="true" />
                    <livewire:operators-view-selector />
                    <livewire:operators-export-options />
                </div>
            </div>
            <livewire:operators-advanced-search />
        </div>
        
        <!-- Content Area -->
        <div class="flex-1 overflow-y-auto">
            @if($view === 'grid')
                <div class="p-6">
                    <div class="grid grid-cols-3 xl:grid-cols-4 gap-6">
                        @foreach($operators as $operator)
                            <livewire:operator-desktop-card :operator="$operator" :comprehensive="true" :key="$operator->id" />
                        @endforeach
                    </div>
                    <div class="mt-8">
                        {{ $operators->links('pagination.desktop') }}
                    </div>
                </div>
            @elseif($view === 'portfolio')
                <div class="p-6 space-y-8">
                    @foreach($operators as $operator)
                        <livewire:operator-portfolio-detailed :operator="$operator" :key="$operator->id" />
                    @endforeach
                </div>
            @elseif($view === 'hierarchy')
                <div class="p-6">
                    <livewire:operators-hierarchy-visualization :operators="$operators" />
                </div>
            @else
                <div class="p-6">
                    <livewire:operators-market-dashboard :operators="$operators" />
                </div>
            @endif
        </div>
    </div>
    
    <!-- Industry Insights Panel -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:operators-industry-insights />
            <div class="mt-6">
                <livewire:operators-market-trends />
            </div>
            <div class="mt-6">
                <livewire:operators-recent-activity />
            </div>
        </div>
    </div>
</div>
```

#### 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.