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 indicatorsDesignerPortfolioShowcase: Comprehensive portfolio display with mediaDesignerInnovationTimeline: Interactive career progression visualizationDesignerCreativeMetrics: Portfolio statistics and creative impact metrics
Context Variations
CoasterDesignersListing: Coaster designers with ride performance metricsThemingExpertsListing: Theming specialists with environmental design focusDarkRideDesignersListing: Dark ride specialists with storytelling emphasisEmergingDesignersListing: 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.