From a57e5deb3f410679c90847fbb055aaaaea6d6cbb Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Tue, 25 Feb 2025 22:14:13 -0500 Subject: [PATCH] feat: implement search functionality with real-time filtering and pagination --- app/Livewire/SearchComponent.php | 136 +++++++++++++ memory-bank/activeContext.md | 41 +++- memory-bank/features/SearchImplementation.md | 139 ++++++++++++++ resources/views/livewire/search.blade.php | 189 +++++++++++++++++++ routes/web.php | 4 +- 5 files changed, 498 insertions(+), 11 deletions(-) create mode 100644 app/Livewire/SearchComponent.php create mode 100644 memory-bank/features/SearchImplementation.md create mode 100644 resources/views/livewire/search.blade.php diff --git a/app/Livewire/SearchComponent.php b/app/Livewire/SearchComponent.php new file mode 100644 index 0000000..1ff1fcc --- /dev/null +++ b/app/Livewire/SearchComponent.php @@ -0,0 +1,136 @@ + ['except' => ''], + 'location' => ['except' => ''], + 'minRating' => ['except' => ''], + 'maxRating' => ['except' => ''], + 'minRides' => ['except' => ''], + 'minCoasters' => ['except' => ''] + ]; + + public function mount(): void + { + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function render(): View + { + return view('livewire.search', [ + 'results' => $this->getFilteredParks() + ]); + } + + public function updatedSearch(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function updatedLocation(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function updatedMinRating(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function updatedMaxRating(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function updatedMinRides(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function updatedMinCoasters(): void + { + $this->resetPage(); + $this->filtersApplied = $this->hasActiveFilters(); + } + + public function clearFilters(): void + { + $this->reset([ + 'search', + 'location', + 'minRating', + 'maxRating', + 'minRides', + 'minCoasters' + ]); + $this->filtersApplied = false; + $this->resetPage(); + } + + protected function getFilteredParks() + { + return Park::query() + ->select('parks.*') + ->with(['location', 'photos']) + ->when($this->search, function (Builder $query) { + $query->where(function (Builder $query) { + $query->where('name', 'like', "%{$this->search}%") + ->orWhere('description', 'like', "%{$this->search}%"); + }); + }) + ->when($this->location, function (Builder $query) { + $query->whereHas('location', function (Builder $query) { + $query->where('address_text', 'like', "%{$this->location}%"); + }); + }) + ->when($this->minRating, function (Builder $query) { + $query->where('average_rating', '>=', $this->minRating); + }) + ->when($this->maxRating, function (Builder $query) { + $query->where('average_rating', '<=', $this->maxRating); + }) + ->when($this->minRides, function (Builder $query) { + $query->where('ride_count', '>=', $this->minRides); + }) + ->when($this->minCoasters, function (Builder $query) { + $query->where('coaster_count', '>=', $this->minCoasters); + }) + ->paginate(10); + } + + protected function hasActiveFilters(): bool + { + return !empty($this->search) + || !empty($this->location) + || !empty($this->minRating) + || !empty($this->maxRating) + || !empty($this->minRides) + || !empty($this->minCoasters); + } +} \ No newline at end of file diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md index e801b3c..10b233b 100644 --- a/memory-bank/activeContext.md +++ b/memory-bank/activeContext.md @@ -1,20 +1,30 @@ # Active Development Context ## Current Task -Implementing ride review components (✅ Completed) +Implementing search functionality (✅ Completed) ## Recent Changes -1. Implemented review components: - - ✅ RideReviewComponent for submitting reviews - - ✅ RideReviewListComponent for displaying reviews - - ✅ ReviewModerationComponent for moderation - - ✅ Updated Memory Bank documentation - - ✅ Committed changes with comprehensive commit message +1. Implemented search functionality: + - ✅ Created SearchComponent with real-time filtering + - ✅ Implemented responsive search UI with filters sidebar + - ✅ Added park cards with dynamic content + - ✅ Integrated dark mode support + - ✅ Added pagination and URL state management + - ✅ Created comprehensive documentation in SearchImplementation.md ## Progress Summary ### Completed Tasks -1. Static Assets Migration +1. Search Implementation + - Created SearchComponent with real-time filtering + - Implemented responsive search UI with filters sidebar + - Added park cards with dynamic content + - Integrated dark mode support + - Added pagination and URL state management + - Created comprehensive documentation + - See `memory-bank/features/SearchImplementation.md` for details + +2. Static Assets Migration - Created directory structure for images, CSS, and JavaScript - Copied images from Django project - Migrated JavaScript modules @@ -58,8 +68,23 @@ Implementing ride review components (✅ Completed) - User menu - Auth menu - Park list with filtering and view modes + - Search with real-time filtering + - Review system with moderation + - Ride management and details ### Next Steps +1. Filament Admin Implementation + - Create admin panel for parks management + - Implement CRUD operations using Filament resources + - Set up role-based access control + - Add audit trails for admin actions + - See `memory-bank/features/FilamentIntegration.md` for details + +2. Analytics Integration + - Implement analytics tracking + - Create statistics dashboard + - Add reporting features + - Set up data aggregation 1. ✅ Park Model Enhancements - ✅ Implemented Photo model and relationship - ✅ Added getBySlug method for historical slug support diff --git a/memory-bank/features/SearchImplementation.md b/memory-bank/features/SearchImplementation.md new file mode 100644 index 0000000..4d5352a --- /dev/null +++ b/memory-bank/features/SearchImplementation.md @@ -0,0 +1,139 @@ +# Search Implementation + +## Overview +The search functionality has been migrated from Django to Laravel/Livewire while maintaining feature parity and improving the user experience with real-time filtering. + +## Key Components + +### SearchComponent (app/Livewire/SearchComponent.php) +- Handles search and filtering logic +- Uses Livewire's real-time search capabilities +- Maintains query parameters in URL +- Implements pagination for results + +#### Filter Properties +- `search`: Text search across name and description +- `location`: Location-based filtering +- `minRating` and `maxRating`: Rating range filtering +- `minRides`: Minimum number of rides filter +- `minCoasters`: Minimum number of coasters filter + +#### Features +- Real-time filtering with `wire:model.live` +- URL query string synchronization +- Eager loading of relationships for performance +- Responsive pagination +- Filter state management + +### View Implementation (resources/views/livewire/search.blade.php) +- Responsive layout with filters sidebar +- Real-time updates without page reload +- Dark mode support +- Accessible form controls +- Mobile-first design + +#### UI Components +1. Filters Sidebar + - Search input + - Location filter + - Rating range inputs + - Ride count filters + - Clear filters button + +2. Results Section + - Results count display + - Park cards with: + * Featured image + * Park name and location + * Rating badge + * Status indicator + * Ride/coaster counts + * Description preview + +## Differences from Django Implementation + +### Improvements +1. Real-time Updates + - Replaced HTMX with Livewire's native reactivity + - Instant filtering without page reloads + - Smoother user experience + +2. State Management + - URL query parameters for shareable searches + - Persistent filter state during navigation + - Clear filters functionality + +3. Performance + - Eager loading of relationships + - Efficient query building + - Optimized view rendering + +### Feature Parity +- Maintained all Django filtering capabilities +- Preserved UI/UX patterns +- Kept identical data presentation +- Matched search algorithm functionality + +## Technical Details + +### Query Building +```php +protected function getFilteredParks() +{ + return Park::query() + ->select('parks.*') + ->with(['location', 'photos']) + ->when($this->search, function (Builder $query) { + $query->where(function (Builder $query) { + $query->where('name', 'like', "%{$this->search}%") + ->orWhere('description', 'like', "%{$this->search}%"); + }); + }) + // Additional filter conditions... + ->paginate(10); +} +``` + +### Filter State Management +```php +protected $queryString = [ + 'search' => ['except' => ''], + 'location' => ['except' => ''], + 'minRating' => ['except' => ''], + 'maxRating' => ['except' => ''], + 'minRides' => ['except' => ''], + 'minCoasters' => ['except' => ''] +]; +``` + +## Testing Considerations +1. Filter Combinations + - Test various filter combinations + - Verify result accuracy + - Check edge cases + +2. Performance Testing + - Large result sets + - Multiple concurrent users + - Query optimization + +3. UI Testing + - Mobile responsiveness + - Dark mode functionality + - Accessibility compliance + +## Future Enhancements +1. Advanced Filters + - Date range filtering + - Category filtering + - Geographic radius search + +2. Performance Optimizations + - Result caching + - Lazy loading options + - Query optimization + +3. UI Improvements + - Save search preferences + - Filter presets + - Advanced sorting options \ No newline at end of file diff --git a/resources/views/livewire/search.blade.php b/resources/views/livewire/search.blade.php new file mode 100644 index 0000000..d0762a2 --- /dev/null +++ b/resources/views/livewire/search.blade.php @@ -0,0 +1,189 @@ +
+
+ +
+
+

Filter Parks

+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ @if($filtersApplied) + + @endif +
+
+
+
+ + +
+
+
+
+

+ Search Results + ({{ $results->total() }} found) +

+
+
+ +
+ @forelse($results as $park) +
+ +
+ @if($park->photos->isNotEmpty()) + {{ $park->name }} + @else +
+ No Image +
+ @endif +
+ + +
+

+ + {{ $park->name }} + +

+ +
+ @if($park->formatted_location) +

{{ $park->formatted_location }}

+ @endif +
+ +
+ @if($park->average_rating) + + {{ number_format($park->average_rating, 1) }} ★ + + @endif + + + {{ $park->status->label() }} + + + @if($park->ride_count) + + {{ $park->ride_count }} Rides + + @endif + + @if($park->coaster_count) + + {{ $park->coaster_count }} Coasters + + @endif +
+ + @if($park->description) +

+ {{ $park->description }} +

+ @endif +
+
+ @empty +
+ No parks found matching your criteria. +
+ @endforelse +
+ + @if($results->hasPages()) +
+ {{ $results->links() }} +
+ @endif +
+
+
+
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 1858df8..b225e52 100644 --- a/routes/web.php +++ b/routes/web.php @@ -79,6 +79,4 @@ Route::get('/profile/{username}', function () { })->name('profile.show'); // Search route -Route::get('/search', function () { - return 'Search'; -})->name('search'); +Route::get('/search', \App\Livewire\SearchComponent::class)->name('search');