Add Road Trip Planner template with interactive map and trip management features

- Implemented a new HTML template for the Road Trip Planner.
- Integrated Leaflet.js for interactive mapping and routing.
- Added functionality for searching and selecting parks to include in a trip.
- Enabled drag-and-drop reordering of selected parks.
- Included trip optimization and route calculation features.
- Created a summary display for trip statistics.
- Added functionality to save trips and manage saved trips.
- Enhanced UI with responsive design and dark mode support.
This commit is contained in:
pacnpal
2025-08-15 20:53:00 -04:00
parent da7c7e3381
commit b5bae44cb8
99 changed files with 18697 additions and 4010 deletions

View File

@@ -1,6 +1,11 @@
from django.views.generic import TemplateView
from django.http import JsonResponse
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance
from parks.models import Park
from parks.filters import ParkFilter
from core.services.location_search import location_search_service, LocationSearchFilters
from core.forms.search import LocationSearchForm
class AdaptiveSearchView(TemplateView):
template_name = "core/search/results.html"
@@ -9,7 +14,7 @@ class AdaptiveSearchView(TemplateView):
"""
Get the base queryset, optimized with select_related and prefetch_related
"""
return Park.objects.select_related('owner').prefetch_related(
return Park.objects.select_related('operator', 'property_owner').prefetch_related(
'location',
'photos'
).all()
@@ -27,10 +32,17 @@ class AdaptiveSearchView(TemplateView):
context = super().get_context_data(**kwargs)
filterset = self.get_filterset()
# Check if location-based search is being used
location_search = self.request.GET.get('location_search', '').strip()
near_location = self.request.GET.get('near_location', '').strip()
# Add location search context
context.update({
'results': filterset.qs,
'filters': filterset,
'applied_filters': bool(self.request.GET), # Check if any filters are applied
'is_location_search': bool(location_search or near_location),
'location_search_query': location_search or near_location,
})
return context
@@ -46,3 +58,107 @@ class FilterFormView(TemplateView):
filterset = ParkFilter(self.request.GET, queryset=Park.objects.all())
context['filters'] = filterset
return context
class LocationSearchView(TemplateView):
"""
Enhanced search view with comprehensive location search capabilities.
"""
template_name = "core/search/location_results.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Build search filters from request parameters
filters = self._build_search_filters()
# Perform search
results = location_search_service.search(filters)
# Group results by type for better presentation
grouped_results = {
'parks': [r for r in results if r.content_type == 'park'],
'rides': [r for r in results if r.content_type == 'ride'],
'companies': [r for r in results if r.content_type == 'company'],
}
context.update({
'results': results,
'grouped_results': grouped_results,
'total_results': len(results),
'search_filters': filters,
'has_location_filter': bool(filters.location_point),
'search_form': LocationSearchForm(self.request.GET),
})
return context
def _build_search_filters(self) -> LocationSearchFilters:
"""Build LocationSearchFilters from request parameters."""
form = LocationSearchForm(self.request.GET)
form.is_valid() # Populate cleaned_data
# Parse location coordinates if provided
location_point = None
lat = form.cleaned_data.get('lat')
lng = form.cleaned_data.get('lng')
if lat and lng:
try:
location_point = Point(float(lng), float(lat), srid=4326)
except (ValueError, TypeError):
location_point = None
# Parse location types
location_types = set()
if form.cleaned_data.get('search_parks'):
location_types.add('park')
if form.cleaned_data.get('search_rides'):
location_types.add('ride')
if form.cleaned_data.get('search_companies'):
location_types.add('company')
# If no specific types selected, search all
if not location_types:
location_types = {'park', 'ride', 'company'}
# Parse radius
radius_km = None
radius_str = form.cleaned_data.get('radius_km', '').strip()
if radius_str:
try:
radius_km = float(radius_str)
radius_km = max(1, min(500, radius_km)) # Clamp between 1-500km
except (ValueError, TypeError):
radius_km = None
return LocationSearchFilters(
search_query=form.cleaned_data.get('q', '').strip() or None,
location_point=location_point,
radius_km=radius_km,
location_types=location_types if location_types else None,
country=form.cleaned_data.get('country', '').strip() or None,
state=form.cleaned_data.get('state', '').strip() or None,
city=form.cleaned_data.get('city', '').strip() or None,
park_status=self.request.GET.getlist('park_status') or None,
include_distance=True,
max_results=int(self.request.GET.get('limit', 100))
)
class LocationSuggestionsView(TemplateView):
"""
AJAX endpoint for location search suggestions.
"""
def get(self, request, *args, **kwargs):
query = request.GET.get('q', '').strip()
limit = int(request.GET.get('limit', 10))
if len(query) < 2:
return JsonResponse({'suggestions': []})
try:
suggestions = location_search_service.suggest_locations(query, limit)
return JsonResponse({'suggestions': suggestions})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)