mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 09:31:11 -05:00
Add comprehensive implementation prompts for Reviews and Rides listing pages with Django parity, Laravel/Livewire architecture, and screen-agnostic design principles
This commit is contained in:
426
memory-bank/prompts/RidesListingPagePrompt.md
Normal file
426
memory-bank/prompts/RidesListingPagePrompt.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# Rides Listing Page Implementation Prompt
|
||||
|
||||
## Django Parity Reference
|
||||
**Django Implementation**: `rides/views.py` - `RideListView` (lines 215-278)
|
||||
**Django Template**: `rides/templates/rides/ride_list.html`
|
||||
**Django Features**: Multi-term search, category filtering, manufacturer filtering, status filtering, pagination with HTMX, eager loading optimization
|
||||
|
||||
## Core Implementation Requirements
|
||||
|
||||
### Laravel/Livewire Architecture
|
||||
Generate the rides listing system using ThrillWiki's custom generators:
|
||||
|
||||
```bash
|
||||
# Generate the main listing component with optimizations
|
||||
php artisan make:thrillwiki-livewire RidesListing --paginated --cached --with-tests
|
||||
|
||||
# Generate reusable search suggestions component
|
||||
php artisan make:thrillwiki-livewire RidesSearchSuggestions --reusable --with-tests
|
||||
|
||||
# Generate advanced filters component
|
||||
php artisan make:thrillwiki-livewire RidesFilters --reusable --cached
|
||||
|
||||
# Generate context-aware listing for park-specific rides
|
||||
php artisan make:thrillwiki-livewire ParkRidesListing --paginated --cached --with-tests
|
||||
```
|
||||
|
||||
### Django Parity Features
|
||||
|
||||
#### 1. Search Functionality
|
||||
**Django Implementation**: Multi-term search across:
|
||||
- Ride name (`name__icontains`)
|
||||
- Ride description (`description__icontains`)
|
||||
- Park name (`park__name__icontains`)
|
||||
- Manufacturer name (`manufacturer__name__icontains`)
|
||||
- Designer name (`designer__name__icontains`)
|
||||
|
||||
**Laravel Implementation**:
|
||||
```php
|
||||
public function search($query)
|
||||
{
|
||||
return Ride::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}%")
|
||||
->orWhereHas('park', fn($q) => $q->where('name', 'ilike', "%{$term}%"))
|
||||
->orWhereHas('manufacturer', fn($q) => $q->where('name', 'ilike', "%{$term}%"))
|
||||
->orWhereHas('designer', fn($q) => $q->where('name', 'ilike', "%{$term}%"));
|
||||
});
|
||||
}
|
||||
})
|
||||
->with(['park', 'manufacturer', 'designer', 'photos'])
|
||||
->orderBy('name');
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Advanced Filtering
|
||||
**Django Filters**:
|
||||
- Category (ride_type)
|
||||
- Status (status)
|
||||
- Manufacturer (manufacturer__id)
|
||||
- Opening year range
|
||||
- Height restrictions
|
||||
- Park context (when viewing park-specific rides)
|
||||
|
||||
**Laravel Filters Implementation**:
|
||||
```php
|
||||
public function applyFilters($query, $filters)
|
||||
{
|
||||
return $query
|
||||
->when($filters['category'] ?? null, fn($q, $category) =>
|
||||
$q->where('ride_type', $category))
|
||||
->when($filters['status'] ?? null, fn($q, $status) =>
|
||||
$q->where('status', $status))
|
||||
->when($filters['manufacturer_id'] ?? null, fn($q, $manufacturerId) =>
|
||||
$q->where('manufacturer_id', $manufacturerId))
|
||||
->when($filters['opening_year_from'] ?? null, fn($q, $year) =>
|
||||
$q->where('opening_date', '>=', "{$year}-01-01"))
|
||||
->when($filters['opening_year_to'] ?? null, fn($q, $year) =>
|
||||
$q->where('opening_date', '<=', "{$year}-12-31"))
|
||||
->when($filters['min_height'] ?? null, fn($q, $height) =>
|
||||
$q->where('height_requirement', '>=', $height))
|
||||
->when($filters['max_height'] ?? null, fn($q, $height) =>
|
||||
$q->where('height_requirement', '<=', $height));
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Context-Aware Views
|
||||
**Global Listing**: All rides across all parks
|
||||
**Park-Specific Listing**: Rides filtered by specific park
|
||||
**Category-Specific Listing**: Rides filtered by ride type/category
|
||||
|
||||
### Screen-Agnostic Design Implementation
|
||||
|
||||
#### Mobile Layout (320px - 767px)
|
||||
- **Single Column**: Full-width ride cards
|
||||
- **Touch Targets**: Minimum 44px touch areas
|
||||
- **Gesture Support**: Pull-to-refresh, swipe navigation
|
||||
- **Bottom Navigation**: Sticky filters and search
|
||||
- **Thumb Navigation**: Search and filter controls within thumb reach
|
||||
|
||||
**Mobile Component Structure**:
|
||||
```blade
|
||||
<div class="rides-mobile-layout">
|
||||
<!-- Sticky Search Bar -->
|
||||
<div class="sticky top-0 bg-white dark:bg-gray-900 z-10 p-4">
|
||||
<livewire:rides-search-suggestions />
|
||||
</div>
|
||||
|
||||
<!-- Quick Filters -->
|
||||
<div class="horizontal-scroll p-4">
|
||||
<livewire:rides-quick-filters />
|
||||
</div>
|
||||
|
||||
<!-- Ride Cards -->
|
||||
<div class="space-y-4 p-4">
|
||||
@foreach($rides as $ride)
|
||||
<livewire:ride-mobile-card :ride="$ride" :key="$ride->id" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Mobile Pagination -->
|
||||
<div class="sticky bottom-0 bg-white dark:bg-gray-900 p-4">
|
||||
{{ $rides->links('pagination.mobile') }}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Tablet Layout (768px - 1023px)
|
||||
- **Dual-Pane**: Filter sidebar + main content
|
||||
- **Grid Layout**: 2-column ride cards
|
||||
- **Advanced Filters**: Expandable filter panels
|
||||
- **Touch + Keyboard**: Support both interaction modes
|
||||
|
||||
**Tablet Component Structure**:
|
||||
```blade
|
||||
<div class="rides-tablet-layout flex">
|
||||
<!-- Filter Sidebar -->
|
||||
<div class="w-80 bg-gray-50 dark:bg-gray-800 p-6">
|
||||
<livewire:rides-filters :expanded="true" />
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 p-6">
|
||||
<!-- Search and Sort -->
|
||||
<div class="mb-6 flex items-center space-x-4">
|
||||
<livewire:rides-search-suggestions class="flex-1" />
|
||||
<livewire:rides-sort-selector />
|
||||
</div>
|
||||
|
||||
<!-- Grid Layout -->
|
||||
<div class="grid grid-cols-2 gap-6 mb-6">
|
||||
@foreach($rides as $ride)
|
||||
<livewire:ride-tablet-card :ride="$ride" :key="$ride->id" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{{ $rides->links() }}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Desktop Layout (1024px - 1919px)
|
||||
- **Three-Pane**: Filter sidebar + main content + quick info panel
|
||||
- **Advanced Grid**: 3-4 column layout
|
||||
- **Keyboard Navigation**: Full keyboard shortcuts
|
||||
- **Mouse Interactions**: Hover effects, context menus
|
||||
|
||||
**Desktop Component Structure**:
|
||||
```blade
|
||||
<div class="rides-desktop-layout flex">
|
||||
<!-- Filter Sidebar -->
|
||||
<div class="w-80 bg-gray-50 dark:bg-gray-800 p-6">
|
||||
<livewire:rides-filters :expanded="true" :advanced="true" />
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 p-8">
|
||||
<!-- Advanced Search Bar -->
|
||||
<div class="mb-8 flex items-center space-x-6">
|
||||
<livewire:rides-search-suggestions class="flex-1" :advanced="true" />
|
||||
<livewire:rides-sort-selector :options="$advancedSortOptions" />
|
||||
<livewire:rides-view-selector />
|
||||
</div>
|
||||
|
||||
<!-- Grid Layout -->
|
||||
<div class="grid grid-cols-3 xl:grid-cols-4 gap-6 mb-8">
|
||||
@foreach($rides as $ride)
|
||||
<livewire:ride-desktop-card :ride="$ride" :key="$ride->id" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Advanced Pagination -->
|
||||
{{ $rides->links('pagination.desktop') }}
|
||||
</div>
|
||||
|
||||
<!-- Quick Info Panel -->
|
||||
<div class="w-80 bg-gray-50 dark:bg-gray-800 p-6">
|
||||
<livewire:rides-quick-info />
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Large Screen Layout (1920px+)
|
||||
- **Dashboard Style**: Multi-column layout with statistics
|
||||
- **Ultra-Wide Optimization**: Up to 6-column grid
|
||||
- **Advanced Analytics**: Statistics panels and data visualization
|
||||
- **Multi-Monitor Support**: Optimized for extended displays
|
||||
|
||||
### Performance Optimization Strategy
|
||||
|
||||
#### Caching Implementation
|
||||
```php
|
||||
public function mount()
|
||||
{
|
||||
$this->cachedFilters = Cache::remember(
|
||||
"rides.filters.{$this->currentUser->id}",
|
||||
now()->addHours(1),
|
||||
fn() => $this->loadFilterOptions()
|
||||
);
|
||||
}
|
||||
|
||||
public function getRidesProperty()
|
||||
{
|
||||
$cacheKey = "rides.listing." . md5(serialize([
|
||||
'search' => $this->search,
|
||||
'filters' => $this->filters,
|
||||
'sort' => $this->sort,
|
||||
'page' => $this->page
|
||||
]));
|
||||
|
||||
return Cache::remember($cacheKey, now()->addMinutes(15), function() {
|
||||
return $this->search($this->search)
|
||||
->applyFilters($this->filters)
|
||||
->orderBy($this->sort['column'], $this->sort['direction'])
|
||||
->paginate(24);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Database Optimization
|
||||
```php
|
||||
// Query optimization with eager loading
|
||||
public function optimizedQuery()
|
||||
{
|
||||
return Ride::select([
|
||||
'id', 'name', 'description', 'ride_type', 'status',
|
||||
'park_id', 'manufacturer_id', 'designer_id', 'opening_date',
|
||||
'height_requirement', 'created_at', 'updated_at'
|
||||
])
|
||||
->with([
|
||||
'park:id,name,slug',
|
||||
'manufacturer:id,name,slug',
|
||||
'designer:id,name,slug',
|
||||
'photos' => fn($q) => $q->select(['id', 'ride_id', 'url', 'thumbnail_url'])->limit(1)
|
||||
])
|
||||
->withCount(['reviews', 'favorites']);
|
||||
}
|
||||
```
|
||||
|
||||
### Component Reuse Strategy
|
||||
|
||||
#### Shared Components
|
||||
- **`RidesSearchSuggestions`**: Reusable across all ride-related pages
|
||||
- **`RidesFilters`**: Extensible filter component with device-aware UI
|
||||
- **`RideCard`**: Responsive ride display component
|
||||
- **`RideQuickView`**: Modal/sidebar quick view component
|
||||
|
||||
#### Context Variations
|
||||
- **`GlobalRidesListing`**: All rides across all parks
|
||||
- **`ParkRidesListing`**: Park-specific rides (extends base listing)
|
||||
- **`CategoryRidesListing`**: Category-specific rides (extends base listing)
|
||||
- **`UserFavoriteRides`**: User's favorite rides (extends base listing)
|
||||
|
||||
### Testing Requirements
|
||||
|
||||
#### Feature Tests
|
||||
```php
|
||||
/** @test */
|
||||
public function can_search_rides_across_multiple_fields()
|
||||
{
|
||||
// Test multi-term search across name, description, park, manufacturer
|
||||
$ride = Ride::factory()->create(['name' => 'Space Mountain']);
|
||||
$park = $ride->park;
|
||||
$park->update(['name' => 'Magic Kingdom']);
|
||||
|
||||
Livewire::test(RidesListing::class)
|
||||
->set('search', 'Space Magic')
|
||||
->assertSee($ride->name)
|
||||
->assertSee($park->name);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function filters_rides_by_multiple_criteria()
|
||||
{
|
||||
$coaster = Ride::factory()->create(['ride_type' => 'roller-coaster']);
|
||||
$kiddie = Ride::factory()->create(['ride_type' => 'kiddie']);
|
||||
|
||||
Livewire::test(RidesListing::class)
|
||||
->set('filters.category', 'roller-coaster')
|
||||
->assertSee($coaster->name)
|
||||
->assertDontSee($kiddie->name);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function maintains_django_parity_performance()
|
||||
{
|
||||
Ride::factory()->count(100)->create();
|
||||
|
||||
$start = microtime(true);
|
||||
Livewire::test(RidesListing::class);
|
||||
$end = microtime(true);
|
||||
|
||||
$this->assertLessThan(0.5, $end - $start); // < 500ms initial load
|
||||
}
|
||||
```
|
||||
|
||||
#### Cross-Device Tests
|
||||
```php
|
||||
/** @test */
|
||||
public function renders_appropriately_on_mobile()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->resize(375, 667) // iPhone dimensions
|
||||
->visit('/rides')
|
||||
->assertVisible('.rides-mobile-layout')
|
||||
->assertMissing('.rides-desktop-layout');
|
||||
});
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function supports_touch_gestures_on_tablet()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->resize(768, 1024) // iPad dimensions
|
||||
->visit('/rides')
|
||||
->assertVisible('.rides-tablet-layout')
|
||||
->swipeLeft('.horizontal-scroll')
|
||||
->assertMissing('.rides-mobile-layout');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Targets
|
||||
|
||||
#### Universal Performance Standards
|
||||
- **Initial Load**: < 500ms (Django parity requirement)
|
||||
- **Filter Response**: < 200ms
|
||||
- **Search Response**: < 300ms
|
||||
- **3G Network**: < 3 seconds total page load
|
||||
- **First Contentful Paint**: < 1.5 seconds across all devices
|
||||
|
||||
#### Device-Specific Targets
|
||||
- **Mobile (3G)**: Core functionality in < 3 seconds
|
||||
- **Tablet (WiFi)**: Full functionality in < 2 seconds
|
||||
- **Desktop (Broadband)**: Advanced features in < 1 second
|
||||
- **Large Screen**: Dashboard mode in < 1.5 seconds
|
||||
|
||||
### Success Criteria Checklist
|
||||
|
||||
#### Django Parity Verification
|
||||
- [ ] Multi-term search matches Django behavior exactly
|
||||
- [ ] All Django filters implemented and functional
|
||||
- [ ] Pagination performance matches or exceeds Django
|
||||
- [ ] Eager loading prevents N+1 queries like Django
|
||||
- [ ] Context-aware views work identically to Django
|
||||
|
||||
#### Screen-Agnostic Compliance
|
||||
- [ ] Mobile layout optimized for 320px+ screens
|
||||
- [ ] Tablet layout utilizes dual-pane effectively
|
||||
- [ ] Desktop layout provides advanced functionality
|
||||
- [ ] Large screen layout maximizes available space
|
||||
- [ ] All touch targets meet 44px minimum requirement
|
||||
- [ ] Keyboard navigation works on all layouts
|
||||
|
||||
#### Performance Benchmarks
|
||||
- [ ] Initial load under 500ms (matches Django target)
|
||||
- [ ] Filter/search responses under 200ms
|
||||
- [ ] 3G network performance under 3 seconds
|
||||
- [ ] Memory usage optimized with proper caching
|
||||
- [ ] Database queries optimized with eager loading
|
||||
|
||||
#### Component Reusability
|
||||
- [ ] Search component reusable across ride-related pages
|
||||
- [ ] Filter component extensible for different contexts
|
||||
- [ ] Card components work across all screen sizes
|
||||
- [ ] Modal/sidebar quick view components functional
|
||||
|
||||
#### Testing Coverage
|
||||
- [ ] All Django functionality covered by feature tests
|
||||
- [ ] Performance tests validate speed requirements
|
||||
- [ ] Cross-device browser tests pass
|
||||
- [ ] Component integration tests complete
|
||||
- [ ] User interaction tests cover all form factors
|
||||
|
||||
## Implementation Priority Order
|
||||
|
||||
1. **Generate Base Components** (Day 1)
|
||||
- Use ThrillWiki generators for rapid scaffolding
|
||||
- Implement core search and filter functionality
|
||||
- Set up responsive layouts
|
||||
|
||||
2. **Django Parity Implementation** (Day 2)
|
||||
- Implement exact search behavior
|
||||
- Add all Django filter options
|
||||
- Optimize database queries
|
||||
|
||||
3. **Screen-Agnostic Optimization** (Day 3)
|
||||
- Fine-tune responsive layouts
|
||||
- Implement device-specific features
|
||||
- Add touch and keyboard interactions
|
||||
|
||||
4. **Performance Optimization** (Day 4)
|
||||
- Implement caching strategies
|
||||
- Optimize database queries
|
||||
- Add lazy loading where appropriate
|
||||
|
||||
5. **Testing and Validation** (Day 5)
|
||||
- Complete test suite implementation
|
||||
- Validate Django parity
|
||||
- Verify performance targets
|
||||
|
||||
This prompt ensures complete Django parity while leveraging Laravel/Livewire advantages and maintaining ThrillWiki's screen-agnostic design principles.
|
||||
Reference in New Issue
Block a user