mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 09:31:11 -05:00
624 lines
26 KiB
Markdown
624 lines
26 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
# 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**:
|
|
```php
|
|
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**:
|
|
```php
|
|
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**:
|
|
```blade
|
|
<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**:
|
|
```blade
|
|
<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**:
|
|
```blade
|
|
<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
|
|
```php
|
|
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
|
|
```php
|
|
// 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
|
|
```php
|
|
/** @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
|
|
```php
|
|
/** @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. |