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

1
core/forms/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .search import LocationSearchForm

105
core/forms/search.py Normal file
View File

@@ -0,0 +1,105 @@
from django import forms
from django.utils.translation import gettext_lazy as _
class LocationSearchForm(forms.Form):
"""
A comprehensive search form that includes text search, location-based
search, and content type filtering for a unified search experience.
"""
# Text search query
q = forms.CharField(
required=False,
label=_("Search Query"),
widget=forms.TextInput(attrs={
'placeholder': _("Search parks, rides, companies..."),
'class': 'w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
# Location-based search
location = forms.CharField(
required=False,
label=_("Near Location"),
widget=forms.TextInput(attrs={
'placeholder': _("City, address, or coordinates..."),
'id': 'location-input',
'class': 'w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
# Hidden fields for coordinates
lat = forms.FloatField(required=False, widget=forms.HiddenInput(attrs={'id': 'lat-input'}))
lng = forms.FloatField(required=False, widget=forms.HiddenInput(attrs={'id': 'lng-input'}))
# Search radius
radius_km = forms.ChoiceField(
required=False,
label=_("Search Radius"),
choices=[
('', _("Any distance")),
('5', _("5 km")),
('10', _("10 km")),
('25', _("25 km")),
('50', _("50 km")),
('100', _("100 km")),
('200', _("200 km")),
],
widget=forms.Select(attrs={
'class': 'w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
# Content type filters
search_parks = forms.BooleanField(
required=False,
initial=True,
label=_("Search Parks"),
widget=forms.CheckboxInput(attrs={'class': 'rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700'})
)
search_rides = forms.BooleanField(
required=False,
label=_("Search Rides"),
widget=forms.CheckboxInput(attrs={'class': 'rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700'})
)
search_companies = forms.BooleanField(
required=False,
label=_("Search Companies"),
widget=forms.CheckboxInput(attrs={'class': 'rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700'})
)
# Geographic filters
country = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'placeholder': _("Country"),
'class': 'w-full px-3 py-2 text-sm border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
state = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'placeholder': _("State/Region"),
'class': 'w-full px-3 py-2 text-sm border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
city = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'placeholder': _("City"),
'class': 'w-full px-3 py-2 text-sm border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white'
})
)
def clean(self):
cleaned_data = super().clean()
# If lat/lng are provided, ensure location field is populated for display
lat = cleaned_data.get('lat')
lng = cleaned_data.get('lng')
location = cleaned_data.get('location')
if lat and lng and not location:
cleaned_data['location'] = f"{lat}, {lng}"
return cleaned_data