Files
thrillwiki_laravel/memory-bank/prompts/DesignersListingPagePrompt.md

26 KiB

Designers Listing Page Implementation Prompt

Django Parity Reference

Django Implementation: designers/views.py - DesignerListView (similar patterns to companies views) Django Template: designers/templates/designers/designer_list.html Django Features: Creative portfolio showcases, design specialization filtering, innovation timeline display, collaboration networks, award recognition system

Core Implementation Requirements

Laravel/Livewire Architecture

Generate the designers listing system using ThrillWiki's custom generators:

# Generate main designers listing with creative portfolio support
php artisan make:thrillwiki-livewire DesignersListing --paginated --cached --with-tests

# Generate creative specialization filters
php artisan make:thrillwiki-livewire DesignersSpecializationFilter --reusable --with-tests

# Generate portfolio showcase component
php artisan make:thrillwiki-livewire DesignerPortfolioShowcase --reusable --with-tests

# Generate innovation timeline component
php artisan make:thrillwiki-livewire DesignerInnovationTimeline --reusable --cached

# Generate collaboration network visualization
php artisan make:thrillwiki-livewire DesignerCollaborationNetwork --reusable --with-tests

# Generate awards and recognition display
php artisan make:thrillwiki-livewire DesignerAwardsRecognition --reusable --cached

# Generate design influence analysis
php artisan make:thrillwiki-livewire DesignerInfluenceAnalysis --reusable --with-tests

Django Parity Features

1. Creative Portfolio Search Functionality

Django Implementation: Multi-faceted search across:

  • Designer name (name__icontains)
  • Design specialization (specialization__icontains)
  • Notable innovations (innovations__description__icontains)
  • Career highlights (career_highlights__icontains)
  • Awards and recognition (awards__title__icontains)
  • Collaboration partners (collaborations__partner__name__icontains)

Laravel Implementation:

public function creativePortfolioSearch($query, $specializations = [])
{
    return Designer::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('bio', 'ilike', "%{$term}%")
                            ->orWhere('design_philosophy', 'ilike', "%{$term}%")
                            ->orWhere('career_highlights', 'ilike', "%{$term}%")
                            ->orWhereHas('designed_rides', function($rideQuery) use ($term) {
                                $rideQuery->where('name', 'ilike', "%{$term}%")
                                         ->orWhere('description', 'ilike', "%{$term}%");
                            })
                            ->orWhereHas('awards', function($awardQuery) use ($term) {
                                $awardQuery->where('title', 'ilike', "%{$term}%")
                                          ->orWhere('description', 'ilike', "%{$term}%");
                            })
                            ->orWhereHas('innovations', function($innQuery) use ($term) {
                                $innQuery->where('title', 'ilike', "%{$term}%")
                                        ->orWhere('description', 'ilike', "%{$term}%");
                            });
                });
            }
        })
        ->when($specializations, function ($q) use ($specializations) {
            $q->where(function ($specQuery) use ($specializations) {
                foreach ($specializations as $spec) {
                    $specQuery->orWhereJsonContains('specializations', $spec);
                }
            });
        })
        ->with([
            'designed_rides' => fn($q) => $q->with(['park', 'photos'])->limit(5),
            'awards' => fn($q) => $q->orderBy('year', 'desc')->limit(3),
            'innovations' => fn($q) => $q->orderBy('year', 'desc')->limit(3),
            'collaborations' => fn($q) => $q->with('partner')->limit(5)
        ])
        ->withCount(['designed_rides', 'awards', 'innovations', 'collaborations']);
}

2. Advanced Creative Filtering

Django Filters:

  • Design specialization (coaster_designer, dark_ride_specialist, theming_expert)
  • Experience level (emerging, established, legendary)
  • Innovation era (classic, modern, contemporary, cutting_edge)
  • Career span (active_years range)
  • Award categories (technical, artistic, lifetime_achievement)
  • Collaboration type (solo_artist, team_player, cross_industry)
  • Geographic influence (regional, national, international)

Laravel Filters Implementation:

public function applyCreativeFilters($query, $filters)
{
    return $query
        ->when($filters['specializations'] ?? null, function ($q, $specializations) {
            $q->where(function ($specQuery) use ($specializations) {
                foreach ($specializations as $spec) {
                    $specQuery->orWhereJsonContains('specializations', $spec);
                }
            });
        })
        ->when($filters['experience_level'] ?? null, function ($q, $level) {
            $experienceRanges = [
                'emerging' => [0, 5],
                'established' => [6, 15],
                'veteran' => [16, 25],
                'legendary' => [26, PHP_INT_MAX]
            ];
            if (isset($experienceRanges[$level])) {
                $q->whereRaw('EXTRACT(YEAR FROM NOW()) - career_start_year BETWEEN ? AND ?', 
                            $experienceRanges[$level]);
            }
        })
        ->when($filters['innovation_era'] ?? null, function ($q, $era) {
            $eraRanges = [
                'classic' => [1950, 1979],
                'modern' => [1980, 1999],
                'contemporary' => [2000, 2009],
                'cutting_edge' => [2010, date('Y')]
            ];
            if (isset($eraRanges[$era])) {
                $q->whereHas('innovations', function ($innQuery) use ($eraRanges, $era) {
                    $innQuery->whereBetween('year', $eraRanges[$era]);
                });
            }
        })
        ->when($filters['career_start_from'] ?? null, fn($q, $year) => 
            $q->where('career_start_year', '>=', $year))
        ->when($filters['career_start_to'] ?? null, fn($q, $year) => 
            $q->where('career_start_year', '<=', $year))
        ->when($filters['award_categories'] ?? null, function ($q, $categories) {
            $q->whereHas('awards', function ($awardQuery) use ($categories) {
                $awardQuery->whereIn('category', $categories);
            });
        })
        ->when($filters['collaboration_style'] ?? null, function ($q, $style) {
            switch ($style) {
                case 'solo_artist':
                    $q->whereDoesntHave('collaborations');
                    break;
                case 'team_player':
                    $q->whereHas('collaborations', fn($colQ) => $colQ->where('type', 'team'));
                    break;
                case 'cross_industry':
                    $q->whereHas('collaborations', fn($colQ) => $colQ->where('type', 'cross_industry'));
                    break;
            }
        })
        ->when($filters['geographic_influence'] ?? null, function ($q, $influence) {
            switch ($influence) {
                case 'regional':
                    $q->whereHas('designed_rides', function ($rideQ) {
                        $rideQ->whereHas('park.location', function ($locQ) {
                            $locQ->havingRaw('COUNT(DISTINCT country) = 1');
                        });
                    });
                    break;
                case 'international':
                    $q->whereHas('designed_rides', function ($rideQ) {
                        $rideQ->whereHas('park.location', function ($locQ) {
                            $locQ->havingRaw('COUNT(DISTINCT country) > 3');
                        });
                    });
                    break;
            }
        });
}

3. Innovation Timeline and Portfolio Display

Creative Metrics:

  • Notable ride designs and their impact
  • Innovation timeline with breakthrough moments
  • Awards and industry recognition
  • Collaboration network and partnerships
  • Design philosophy and artistic influence
  • Career milestones and achievements

Screen-Agnostic Design Implementation

Mobile Layout (320px - 767px)

  • Designer Cards: Artist-focused cards with signature designs
  • Portfolio Highlights: Visual showcase of most notable works
  • Innovation Badges: Visual indicators of breakthrough innovations
  • Timeline Snapshots: Condensed career timeline view

Mobile Component Structure:

<div class="designers-mobile-layout">
    <!-- Creative Search Bar -->
    <div class="sticky top-0 bg-white dark:bg-gray-900 z-20 p-4">
        <livewire:designers-creative-search />
        <div class="flex items-center mt-2 space-x-2">
            <button wire:click="filterBySpecialization('coaster_designer')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeSpec === 'coaster_designer' ? 'bg-red-500 text-white' : 'bg-red-100 dark:bg-red-900' }} rounded-full">
                <span class="text-sm">Coasters</span>
            </button>
            <button wire:click="filterBySpecialization('dark_ride_specialist')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeSpec === 'dark_ride_specialist' ? 'bg-purple-500 text-white' : 'bg-purple-100 dark:bg-purple-900' }} rounded-full">
                <span class="text-sm">Dark Rides</span>
            </button>
            <button wire:click="filterBySpecialization('theming_expert')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeSpec === 'theming_expert' ? 'bg-green-500 text-white' : 'bg-green-100 dark:bg-green-900' }} rounded-full">
                <span class="text-sm">Theming</span>
            </button>
        </div>
    </div>
    
    <!-- Creative Inspiration Banner -->
    <div class="bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white p-4 m-4 rounded-lg">
        <livewire:designers-inspiration-stats :compact="true" />
    </div>
    
    <!-- Quick Filters -->
    <div class="horizontal-scroll p-4 pb-2">
        <livewire:designers-quick-filters />
    </div>
    
    <!-- Designer Cards -->
    <div class="space-y-4 p-4">
        @foreach($designers as $designer)
            <livewire:designer-mobile-card :designer="$designer" :show-portfolio="true" :key="$designer->id" />
        @endforeach
    </div>
    
    <!-- Mobile Pagination -->
    <div class="sticky bottom-0 bg-white dark:bg-gray-900 p-4">
        {{ $designers->links('pagination.mobile') }}
    </div>
</div>

Tablet Layout (768px - 1023px)

  • Portfolio Gallery: Visual grid of signature designs
  • Innovation Timeline: Interactive career progression
  • Collaboration Network: Visual relationship mapping
  • Awards Showcase: Comprehensive recognition display

Tablet Component Structure:

<div class="designers-tablet-layout flex h-screen">
    <!-- Creative Filter Sidebar -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:designers-creative-search :advanced="true" />
            <div class="mt-6">
                <livewire:designers-specialization-filter :expanded="true" />
            </div>
            <div class="mt-6">
                <livewire:designers-creative-filters :show-awards="true" />
            </div>
            <div class="mt-6">
                <livewire:designers-inspiration-stats :detailed="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content Area -->
    <div class="flex-1 flex flex-col">
        <!-- Creative 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">{{ $designers->total() }} Visionary Designers</h2>
                    <livewire:designers-industry-overview />
                </div>
                <div class="flex items-center space-x-2">
                    <livewire:designers-sort-selector />
                    <livewire:designers-view-toggle />
                </div>
            </div>
        </div>
        
        <!-- Content Display -->
        <div class="flex-1 overflow-y-auto p-6">
            @if($view === 'grid')
                <div class="grid grid-cols-2 gap-6">
                    @foreach($designers as $designer)
                        <livewire:designer-tablet-card :designer="$designer" :portfolio="true" :key="$designer->id" />
                    @endforeach
                </div>
            @elseif($view === 'timeline')
                <div class="space-y-8">
                    @foreach($designers as $designer)
                        <livewire:designer-timeline-showcase :designer="$designer" :key="$designer->id" />
                    @endforeach
                </div>
            @else
                <livewire:designers-innovation-analysis :designers="$designers" />
            @endif
            
            <div class="mt-6">
                {{ $designers->links() }}
            </div>
        </div>
    </div>
</div>

Desktop Layout (1024px - 1919px)

  • Comprehensive Portfolio Views: Detailed design showcases
  • Interactive Innovation Timeline: Full career progression with milestones
  • Collaboration Network Visualization: Complex relationship mapping
  • Creative Influence Analysis: Industry impact visualization

Desktop Component Structure:

<div class="designers-desktop-layout flex h-screen">
    <!-- Advanced Creative Filters -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:designers-creative-search :advanced="true" :autocomplete="true" />
            <div class="mt-6">
                <livewire:designers-specialization-filter :advanced="true" :show-statistics="true" />
            </div>
            <div class="mt-6">
                <livewire:designers-creative-filters :advanced="true" :show-awards="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content -->
    <div class="flex-1 flex flex-col">
        <!-- Creative 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">{{ $designers->total() }} Creative Visionaries</h1>
                    <livewire:designers-creative-summary />
                </div>
                <div class="flex items-center space-x-4">
                    <livewire:designers-sort-selector :advanced="true" />
                    <livewire:designers-view-selector />
                    <livewire:designers-export-options />
                </div>
            </div>
            <livewire:designers-advanced-search />
        </div>
        
        <!-- Content Area -->
        <div class="flex-1 overflow-y-auto">
            @if($view === 'portfolio')
                <div class="p-6">
                    <div class="grid grid-cols-3 xl:grid-cols-4 gap-6">
                        @foreach($designers as $designer)
                            <livewire:designer-desktop-card :designer="$designer" :comprehensive="true" :key="$designer->id" />
                        @endforeach
                    </div>
                    <div class="mt-8">
                        {{ $designers->links('pagination.desktop') }}
                    </div>
                </div>
            @elseif($view === 'timeline')
                <div class="p-6 space-y-8">
                    @foreach($designers as $designer)
                        <livewire:designer-innovation-timeline :designer="$designer" :detailed="true" :key="$designer->id" />
                    @endforeach
                </div>
            @elseif($view === 'network')
                <div class="p-6">
                    <livewire:designer-collaboration-network :designers="$designers" :interactive="true" />
                </div>
            @else
                <div class="p-6">
                    <livewire:designers-creative-dashboard :designers="$designers" />
                </div>
            @endif
        </div>
    </div>
    
    <!-- Creative Insights Panel -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:designers-creative-insights />
            <div class="mt-6">
                <livewire:designers-innovation-trends />
            </div>
            <div class="mt-6">
                <livewire:designers-featured-works />
            </div>
        </div>
    </div>
</div>

Large Screen Layout (1920px+)

  • Creative Studio Interface: Comprehensive design analysis
  • Multi-Panel Innovation Views: Simultaneous portfolio and timeline analysis
  • Advanced Visualization: Creative influence networks and innovation patterns
  • Immersive Portfolio Experience: Full-screen design showcases

Performance Optimization Strategy

Creative Portfolio Caching

public function mount()
{
    $this->creativeStats = Cache::remember(
        'designers.creative.stats',
        now()->addHours(8),
        fn() => $this->calculateCreativeStatistics()
    );
    
    $this->innovationTrends = Cache::remember(
        'designers.innovation.trends',
        now()->addHours(24),
        fn() => $this->loadInnovationTrends()
    );
}

public function getDesignersProperty()
{
    $cacheKey = "designers.listing." . md5(serialize([
        'search' => $this->search,
        'filters' => $this->filters,
        'specialization_filter' => $this->specializationFilter,
        'sort' => $this->sort,
        'page' => $this->page
    ]));
    
    return Cache::remember($cacheKey, now()->addMinutes(45), function() {
        return $this->creativePortfolioSearch($this->search, $this->specializationFilter)
            ->applyCreativeFilters($this->filters)
            ->orderBy($this->sort['column'], $this->sort['direction'])
            ->paginate(16);
    });
}

Portfolio Media Optimization

// Optimized query for portfolio and innovation data
public function optimizedPortfolioQuery()
{
    return Designer::select([
        'designers.*',
        DB::raw('COALESCE(rides_count.count, 0) as designed_rides_count'),
        DB::raw('COALESCE(awards_count.count, 0) as awards_count'),
        DB::raw('COALESCE(innovations_count.count, 0) as innovations_count'),
        DB::raw('CASE 
            WHEN EXTRACT(YEAR FROM NOW()) - career_start_year > 25 THEN "legendary"
            WHEN EXTRACT(YEAR FROM NOW()) - career_start_year > 15 THEN "veteran"
            WHEN EXTRACT(YEAR FROM NOW()) - career_start_year > 5 THEN "established"
            ELSE "emerging"
        END as experience_level_category')
    ])
    ->leftJoin(DB::raw('(SELECT designer_id, COUNT(*) as count FROM rides GROUP BY designer_id) as rides_count'), 
              'designers.id', '=', 'rides_count.designer_id')
    ->leftJoin(DB::raw('(SELECT designer_id, COUNT(*) as count FROM designer_awards GROUP BY designer_id) as awards_count'), 
              'designers.id', '=', 'awards_count.designer_id')
    ->leftJoin(DB::raw('(SELECT designer_id, COUNT(*) as count FROM designer_innovations GROUP BY designer_id) as innovations_count'), 
              'designers.id', '=', 'innovations_count.designer_id')
    ->with([
        'designed_rides:id,designer_id,name,ride_type,opening_date',
        'awards:id,designer_id,title,year,category',
        'innovations:id,designer_id,title,year,description',
        'collaborations' => fn($q) => $q->with('partner:id,name,type')
    ]);
}

Component Reuse Strategy

Shared Components

  • DesignersSpecializationFilter: Multi-specialization filtering with visual indicators
  • DesignerPortfolioShowcase: Comprehensive portfolio display with media
  • DesignerInnovationTimeline: Interactive career progression visualization
  • DesignerCreativeMetrics: Portfolio statistics and creative impact metrics

Context Variations

  • CoasterDesignersListing: Coaster designers with ride performance metrics
  • ThemingExpertsListing: Theming specialists with environmental design focus
  • DarkRideDesignersListing: Dark ride specialists with storytelling emphasis
  • EmergingDesignersListing: New talent showcase with potential indicators

Testing Requirements

Feature Tests

/** @test */
public function can_filter_designers_by_specialization()
{
    $coasterDesigner = Designer::factory()->create([
        'name' => 'John Wardley',
        'specializations' => ['coaster_designer', 'theming_expert']
    ]);
    $coasterDesigner->designed_rides()->create(['name' => 'The Smiler', 'ride_type' => 'roller-coaster']);
    
    $darkRideDesigner = Designer::factory()->create([
        'name' => 'Tony Baxter',
        'specializations' => ['dark_ride_specialist', 'imagineer']
    ]);
    $darkRideDesigner->designed_rides()->create(['name' => 'Indiana Jones Adventure', 'ride_type' => 'dark-ride']);
    
    Livewire::test(DesignersListing::class)
        ->set('specializationFilter', ['coaster_designer'])
        ->assertSee($coasterDesigner->name)
        ->assertDontSee($darkRideDesigner->name);
}

/** @test */
public function calculates_experience_level_correctly()
{
    $legendary = Designer::factory()->create(['career_start_year' => 1985]);
    $veteran = Designer::factory()->create(['career_start_year' => 2000]);
    $established = Designer::factory()->create(['career_start_year' => 2010]);
    $emerging = Designer::factory()->create(['career_start_year' => 2020]);
    
    $component = Livewire::test(DesignersListing::class);
    $designers = $component->get('designers');
    
    $this->assertEquals('legendary', $designers->where('id', $legendary->id)->first()->experience_level_category);
    $this->assertEquals('veteran', $designers->where('id', $veteran->id)->first()->experience_level_category);
}

/** @test */
public function maintains_django_parity_performance_with_portfolio_data()
{
    Designer::factory()->count(40)->create();
    
    $start = microtime(true);
    Livewire::test(DesignersListing::class);
    $end = microtime(true);
    
    $this->assertLessThan(0.5, $end - $start); // < 500ms with portfolio data
}

Creative Portfolio Tests

/** @test */
public function displays_portfolio_metrics_accurately()
{
    $designer = Designer::factory()->create();
    $designer->designed_rides()->createMany(8, ['name' => 'Test Ride']);
    $designer->awards()->createMany(3, ['title' => 'Test Award']);
    $designer->innovations()->createMany(2, ['title' => 'Test Innovation']);
    
    $component = Livewire::test(DesignersListing::class);
    $portfolioData = $component->get('designers')->first();
    
    $this->assertEquals(8, $portfolioData->designed_rides_count);
    $this->assertEquals(3, $portfolioData->awards_count);
    $this->assertEquals(2, $portfolioData->innovations_count);
}

/** @test */
public function handles_collaboration_network_visualization()
{
    $designer1 = Designer::factory()->create(['name' => 'Designer One']);
    $designer2 = Designer::factory()->create(['name' => 'Designer Two']);
    
    $designer1->collaborations()->create([
        'partner_id' => $designer2->id,
        'type' => 'team',
        'project_name' => 'Joint Project'
    ]);
    
    $component = Livewire::test(DesignersListing::class);
    $collaborationData = $component->get('designers')->first()->collaborations;
    
    $this->assertCount(1, $collaborationData);
    $this->assertEquals('Joint Project', $collaborationData->first()->project_name);
}

Performance Targets

Universal Performance Standards with Creative Content

  • Initial Load: < 500ms (including portfolio thumbnails)
  • Portfolio Rendering: < 300ms for 20 designers
  • Innovation Timeline: < 200ms for complex career data
  • Collaboration Network: < 1 second for network visualization
  • Creative Statistics: < 150ms (cached)

Creative Content Caching Strategy

  • Innovation Trends: 24 hours (industry trends stable)
  • Creative Statistics: 8 hours (portfolio metrics change)
  • Portfolio Thumbnails: 48 hours (visual content stable)
  • Designer Profiles: 12 hours (career data relatively stable)

Success Criteria Checklist

Django Parity Verification

  • Creative portfolio search matches Django behavior exactly
  • Specialization filtering provides same results as Django
  • Innovation timeline displays identically to Django
  • Awards and recognition match Django structure
  • Collaboration networks visualize like Django implementation

Screen-Agnostic Compliance

  • Mobile layout optimized for creative content consumption
  • Tablet layout provides effective portfolio browsing
  • Desktop layout maximizes creative visualization
  • Large screen layout provides immersive portfolio experience
  • All layouts handle rich media content gracefully

Performance Benchmarks

  • Initial load under 500ms including portfolio media
  • Portfolio rendering under 300ms
  • Innovation timeline under 200ms
  • Creative statistics under 150ms (cached)
  • Portfolio caching reduces server load by 65%

Creative Feature Completeness

  • Specialization filtering works across all design disciplines
  • Portfolio showcases provide comprehensive creative overviews
  • Innovation timelines visualize career progression accurately
  • Collaboration networks display meaningful relationships
  • Awards and recognition systems provide proper attribution

This prompt ensures complete Django parity while providing comprehensive creative portfolio capabilities that showcase designer talent and innovation while maintaining ThrillWiki's screen-agnostic design principles.