mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-03-30 23:48:23 -04:00
- Refactored park detail template from HTMX/Alpine.js to Django Unicorn component
- Achieved ~97% reduction in template complexity
- Created ParkDetailView component with optimized data loading and reactive features
- Developed a responsive reactive template for park details
- Implemented server-side state management and reactive event handlers
- Enhanced performance with optimized database queries and loading states
- Comprehensive error handling and user experience improvements
docs: Update Django Unicorn refactoring plan with completed components and phases
- Documented installation and configuration of Django Unicorn
- Detailed completed work on park search component and refactoring strategy
- Outlined planned refactoring phases for future components
- Provided examples of component structure and usage
feat: Implement parks rides endpoint with comprehensive features
- Developed API endpoint GET /api/v1/parks/{park_slug}/rides/ for paginated ride listings
- Included filtering capabilities for categories and statuses
- Optimized database queries with select_related and prefetch_related
- Implemented serializer for comprehensive ride data output
- Added complete API documentation for frontend integration
137 lines
4.0 KiB
Python
137 lines
4.0 KiB
Python
from django_unicorn.components import UnicornView
|
|
from django.db.models import Q
|
|
from django.core.paginator import Paginator
|
|
from apps.parks.models import Park
|
|
|
|
|
|
class ParkSearchView(UnicornView):
|
|
"""
|
|
Reactive park search component that replaces HTMX functionality.
|
|
Provides real-time search, filtering, and view mode switching.
|
|
"""
|
|
|
|
# Search and filter state
|
|
search_query: str = ""
|
|
view_mode: str = "grid" # "grid" or "list"
|
|
|
|
# Results state
|
|
parks = [] # Use list instead of QuerySet for caching compatibility
|
|
total_results: int = 0
|
|
page: int = 1
|
|
per_page: int = 12
|
|
|
|
# Loading state
|
|
is_loading: bool = False
|
|
|
|
def mount(self):
|
|
"""Initialize component with all parks"""
|
|
self.load_parks()
|
|
|
|
def load_parks(self):
|
|
"""Load parks based on current search and filters"""
|
|
self.is_loading = True
|
|
|
|
# Start with all parks
|
|
queryset = Park.objects.select_related(
|
|
'operator', 'property_owner', 'location'
|
|
).prefetch_related('photos')
|
|
|
|
# Apply search filter
|
|
if self.search_query.strip():
|
|
search_terms = self.search_query.strip().split()
|
|
search_q = Q()
|
|
|
|
for term in search_terms:
|
|
term_q = (
|
|
Q(name__icontains=term) |
|
|
Q(description__icontains=term) |
|
|
Q(location__city__icontains=term) |
|
|
Q(location__state__icontains=term) |
|
|
Q(location__country__icontains=term) |
|
|
Q(operator__name__icontains=term)
|
|
)
|
|
search_q &= term_q
|
|
|
|
queryset = queryset.filter(search_q)
|
|
|
|
# Order by name
|
|
queryset = queryset.order_by('name')
|
|
|
|
# Get total count
|
|
self.total_results = queryset.count()
|
|
|
|
# Apply pagination
|
|
paginator = Paginator(queryset, self.per_page)
|
|
page_obj = paginator.get_page(self.page)
|
|
|
|
# Convert to list for caching compatibility
|
|
self.parks = list(page_obj.object_list)
|
|
self.is_loading = False
|
|
|
|
def updated_search_query(self, query):
|
|
"""Called when search query changes"""
|
|
self.search_query = query
|
|
self.page = 1 # Reset to first page
|
|
self.load_parks()
|
|
|
|
def set_view_mode(self, mode):
|
|
"""Switch between grid and list view modes"""
|
|
if mode in ['grid', 'list']:
|
|
self.view_mode = mode
|
|
|
|
def clear_search(self):
|
|
"""Clear search query and reload all parks"""
|
|
self.search_query = ""
|
|
self.page = 1
|
|
self.load_parks()
|
|
|
|
def next_page(self):
|
|
"""Go to next page"""
|
|
if self.has_next_page():
|
|
self.page += 1
|
|
self.load_parks()
|
|
|
|
def previous_page(self):
|
|
"""Go to previous page"""
|
|
if self.has_previous_page():
|
|
self.page -= 1
|
|
self.load_parks()
|
|
|
|
def go_to_page(self, page_num):
|
|
"""Go to specific page"""
|
|
if 1 <= page_num <= self.total_pages():
|
|
self.page = page_num
|
|
self.load_parks()
|
|
|
|
def has_next_page(self):
|
|
"""Check if there's a next page"""
|
|
return self.page < self.total_pages()
|
|
|
|
def has_previous_page(self):
|
|
"""Check if there's a previous page"""
|
|
return self.page > 1
|
|
|
|
def total_pages(self):
|
|
"""Calculate total number of pages"""
|
|
if self.total_results == 0:
|
|
return 1
|
|
return (self.total_results + self.per_page - 1) // self.per_page
|
|
|
|
def get_page_range(self):
|
|
"""Get range of page numbers for pagination"""
|
|
total = self.total_pages()
|
|
current = self.page
|
|
|
|
# Show 5 pages around current page
|
|
start = max(1, current - 2)
|
|
end = min(total, current + 2)
|
|
|
|
# Adjust if we're near the beginning or end
|
|
if end - start < 4:
|
|
if start == 1:
|
|
end = min(total, start + 4)
|
|
else:
|
|
start = max(1, end - 4)
|
|
|
|
return list(range(start, end + 1))
|