mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 18:11:08 -05:00
idk man
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -66,17 +66,7 @@ class Park(models.Model):
|
|||||||
self.slug = slugify(self.name)
|
self.slug = slugify(self.name)
|
||||||
|
|
||||||
# Update the location field to combine country, region, and city
|
# Update the location field to combine country, region, and city
|
||||||
location_parts = []
|
self.location = self.get_formatted_location()
|
||||||
if self.city:
|
|
||||||
location_parts.append(self.city.name)
|
|
||||||
if self.region:
|
|
||||||
location_parts.append(self.region.name)
|
|
||||||
if self.country:
|
|
||||||
location_parts.append(self.country.name)
|
|
||||||
|
|
||||||
# Only update location if we have parts to combine
|
|
||||||
if location_parts:
|
|
||||||
self.location = ', '.join(location_parts)
|
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@@ -93,15 +83,15 @@ class Park(models.Model):
|
|||||||
raise cls.DoesNotExist("No park found with this slug")
|
raise cls.DoesNotExist("No park found with this slug")
|
||||||
|
|
||||||
def get_formatted_location(self):
|
def get_formatted_location(self):
|
||||||
"""Get a formatted location string combining city, region, and country"""
|
"""Get a formatted location string: $COUNTRY, $REGION, $CITY"""
|
||||||
location_parts = []
|
location = self.country.name
|
||||||
if self.city:
|
|
||||||
location_parts.append(self.city.name)
|
if self.region and self.city:
|
||||||
if self.region:
|
location += f", {self.region.name}, {self.city.name}"
|
||||||
location_parts.append(self.region.name)
|
elif self.region:
|
||||||
if self.country:
|
location += f", {self.region.name}"
|
||||||
location_parts.append(self.country.name)
|
|
||||||
return ', '.join(location_parts)
|
return location
|
||||||
|
|
||||||
class ParkArea(models.Model):
|
class ParkArea(models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ urlpatterns = [
|
|||||||
path('ajax/countries/', views.get_countries, name='get_countries'),
|
path('ajax/countries/', views.get_countries, name='get_countries'),
|
||||||
path('ajax/regions/', views.get_regions, name='get_regions'),
|
path('ajax/regions/', views.get_regions, name='get_regions'),
|
||||||
path('ajax/cities/', views.get_cities, name='get_cities'),
|
path('ajax/cities/', views.get_cities, name='get_cities'),
|
||||||
path('ajax/locations/', views.get_locations, name='get_locations'),
|
|
||||||
path('<slug:slug>/', views.ParkDetailView.as_view(), name='park_detail'),
|
path('<slug:slug>/', views.ParkDetailView.as_view(), name='park_detail'),
|
||||||
path('<slug:park_slug>/rides/', include('rides.urls', namespace='rides')),
|
path('<slug:park_slug>/rides/', include('rides.urls', namespace='rides')),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -45,24 +45,6 @@ def get_cities(request):
|
|||||||
).values_list('name', flat=True)[:10]
|
).values_list('name', flat=True)[:10]
|
||||||
return JsonResponse(list(cities), safe=False)
|
return JsonResponse(list(cities), safe=False)
|
||||||
|
|
||||||
def get_locations(request):
|
|
||||||
query = request.GET.get('q', '')
|
|
||||||
locations = set()
|
|
||||||
|
|
||||||
# Search countries
|
|
||||||
countries = Country.objects.filter(name__icontains=query).values_list('name', flat=True)[:5]
|
|
||||||
locations.update(countries)
|
|
||||||
|
|
||||||
# Search regions
|
|
||||||
regions = Region.objects.filter(name__icontains=query).values_list('name', flat=True)[:5]
|
|
||||||
locations.update(regions)
|
|
||||||
|
|
||||||
# Search cities
|
|
||||||
cities = City.objects.filter(name__icontains=query).values_list('name', flat=True)[:5]
|
|
||||||
locations.update(cities)
|
|
||||||
|
|
||||||
return JsonResponse(list(locations), safe=False)
|
|
||||||
|
|
||||||
class ParkCreateView(LoginRequiredMixin, CreateView):
|
class ParkCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = Park
|
model = Park
|
||||||
form_class = ParkForm
|
form_class = ParkForm
|
||||||
@@ -163,26 +145,27 @@ class ParkListView(ListView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = Park.objects.select_related('owner', 'country', 'region', 'city').prefetch_related('photos', 'rides')
|
queryset = Park.objects.select_related('owner', 'country', 'region', 'city').prefetch_related('photos', 'rides')
|
||||||
|
|
||||||
search = self.request.GET.get('search', '').strip() or None
|
search = self.request.GET.get('search', '').strip()
|
||||||
location = self.request.GET.get('location', '').strip() or None
|
country = self.request.GET.get('country', '').strip()
|
||||||
status = self.request.GET.get('status', '').strip() or None
|
region = self.request.GET.get('region', '').strip()
|
||||||
|
city = self.request.GET.get('city', '').strip()
|
||||||
|
status = self.request.GET.get('status', '').strip()
|
||||||
|
|
||||||
if search:
|
if search:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(name__icontains=search) |
|
Q(name__icontains=search) |
|
||||||
Q(location__icontains=search) |
|
Q(location__icontains=search)
|
||||||
Q(country__name__icontains=search) |
|
|
||||||
Q(region__name__icontains=search) |
|
|
||||||
Q(city__name__icontains=search)
|
|
||||||
)
|
|
||||||
if location:
|
|
||||||
# Try to match against the formatted location or any location field
|
|
||||||
queryset = queryset.filter(
|
|
||||||
Q(location__icontains=location) |
|
|
||||||
Q(country__name__icontains=location) |
|
|
||||||
Q(region__name__icontains=location) |
|
|
||||||
Q(city__name__icontains=location)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if country:
|
||||||
|
queryset = queryset.filter(country__name__icontains=country)
|
||||||
|
|
||||||
|
if region:
|
||||||
|
queryset = queryset.filter(region__name__icontains=region)
|
||||||
|
|
||||||
|
if city:
|
||||||
|
queryset = queryset.filter(city__name__icontains=city)
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
queryset = queryset.filter(status=status)
|
queryset = queryset.filter(status=status)
|
||||||
|
|
||||||
@@ -192,7 +175,9 @@ class ParkListView(ListView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['current_filters'] = {
|
context['current_filters'] = {
|
||||||
'search': self.request.GET.get('search', ''),
|
'search': self.request.GET.get('search', ''),
|
||||||
'location': self.request.GET.get('location', ''),
|
'country': self.request.GET.get('country', ''),
|
||||||
|
'region': self.request.GET.get('region', ''),
|
||||||
|
'city': self.request.GET.get('city', ''),
|
||||||
'status': self.request.GET.get('status', '')
|
'status': self.request.GET.get('status', '')
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -3491,6 +3491,10 @@ select {
|
|||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg\:grid-cols-5 {
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.lg\:text-6xl {
|
.lg\:text-6xl {
|
||||||
font-size: 3.75rem;
|
font-size: 3.75rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -18,8 +18,7 @@
|
|||||||
data-field-name="name">{{ park.name }}</h1>
|
data-field-name="name">{{ park.name }}</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-300">
|
<p class="text-gray-600 dark:text-gray-300">
|
||||||
<i class="mr-2 fas fa-map-marker-alt"></i>
|
<i class="mr-2 fas fa-map-marker-alt"></i>
|
||||||
<span data-editable data-content-id="{{ park.id }}"
|
<span>{{ park.get_formatted_location }}</span>
|
||||||
data-field-name="location">{{ park.location }}</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 mt-4 md:mt-0">
|
<div class="flex gap-2 mt-4 md:mt-0">
|
||||||
@@ -201,12 +200,10 @@
|
|||||||
<div class="p-4 transition-transform transform rounded-lg bg-gray-50 dark:bg-gray-700/50 hover:-translate-y-1">
|
<div class="p-4 transition-transform transform rounded-lg bg-gray-50 dark:bg-gray-700/50 hover:-translate-y-1">
|
||||||
<dt class="flex items-center mb-1 text-gray-600 dark:text-gray-300">
|
<dt class="flex items-center mb-1 text-gray-600 dark:text-gray-300">
|
||||||
<i class="w-5 text-blue-500 fas fa-globe dark:text-blue-400"></i>
|
<i class="w-5 text-blue-500 fas fa-globe dark:text-blue-400"></i>
|
||||||
<span class="ml-2">Country</span>
|
<span class="ml-2">Location</span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="font-medium text-gray-900 dark:text-white"
|
<dd class="font-medium text-gray-900 dark:text-white">
|
||||||
data-editable data-content-id="{{ park.id }}"
|
{{ park.get_formatted_location }}
|
||||||
data-field-name="country">
|
|
||||||
{{ park.get_country_name }}
|
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
{% if park.opening_date %}
|
{% if park.opening_date %}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
<!-- Filters -->
|
<!-- Filters -->
|
||||||
<div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
<div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||||
<form class="grid grid-cols-1 gap-4 md:grid-cols-3"
|
<form id="park-filters" class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-5"
|
||||||
hx-get="{% url 'parks:park_list' %}"
|
hx-get="{% url 'parks:park_list' %}"
|
||||||
hx-trigger="change from:select, input[type='text'] delay:500ms"
|
hx-trigger="change from:select, input from:input[type='text'] delay:500ms"
|
||||||
hx-target="#parks-grid"
|
hx-target="#parks-grid"
|
||||||
hx-push-url="true">
|
hx-push-url="true">
|
||||||
<div>
|
<div>
|
||||||
@@ -29,11 +29,25 @@
|
|||||||
placeholder="Search parks...">
|
placeholder="Search parks...">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="location" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Location</label>
|
<label for="country" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Country</label>
|
||||||
<input type="text" name="location" id="location"
|
<input type="text" name="country" id="country"
|
||||||
value="{{ current_filters.location }}"
|
value="{{ current_filters.country }}"
|
||||||
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||||
placeholder="Search locations...">
|
placeholder="Select country...">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="region" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">State/Region</label>
|
||||||
|
<input type="text" name="region" id="region"
|
||||||
|
value="{{ current_filters.region }}"
|
||||||
|
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||||
|
placeholder="Select state/region...">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="city" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">City</label>
|
||||||
|
<input type="text" name="city" id="city"
|
||||||
|
value="{{ current_filters.city }}"
|
||||||
|
class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||||
|
placeholder="Select city...">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="status" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Status</label>
|
<label for="status" class="block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">Status</label>
|
||||||
@@ -84,27 +98,79 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var locationInput = document.getElementById('location');
|
const countryInput = document.getElementById('country');
|
||||||
if (locationInput) {
|
const regionInput = document.getElementById('region');
|
||||||
var locationList = new Awesomplete(locationInput, {
|
const cityInput = document.getElementById('city');
|
||||||
|
|
||||||
|
// Initialize Awesomplete for country
|
||||||
|
if (countryInput) {
|
||||||
|
const countryList = new Awesomplete(countryInput, {
|
||||||
minChars: 1,
|
minChars: 1,
|
||||||
maxItems: 10,
|
maxItems: 10,
|
||||||
autoFirst: true
|
autoFirst: true
|
||||||
});
|
});
|
||||||
|
|
||||||
locationInput.addEventListener('input', function() {
|
countryInput.addEventListener('input', function() {
|
||||||
fetch(`/parks/ajax/locations/?q=${encodeURIComponent(this.value)}`)
|
fetch(`/parks/ajax/countries/?q=${encodeURIComponent(this.value)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
locationList.list = data;
|
countryList.list = data;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger HTMX request when a location is selected
|
// Initialize Awesomplete for region
|
||||||
locationInput.addEventListener('awesomplete-select', function(event) {
|
if (regionInput) {
|
||||||
htmx.trigger(this, 'change');
|
const regionList = new Awesomplete(regionInput, {
|
||||||
|
minChars: 1,
|
||||||
|
maxItems: 10,
|
||||||
|
autoFirst: true
|
||||||
|
});
|
||||||
|
|
||||||
|
regionInput.addEventListener('input', function() {
|
||||||
|
const country = countryInput.value;
|
||||||
|
fetch(`/parks/ajax/regions/?q=${encodeURIComponent(this.value)}&country=${encodeURIComponent(country)}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
regionList.list = data;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize Awesomplete for city
|
||||||
|
if (cityInput) {
|
||||||
|
const cityList = new Awesomplete(cityInput, {
|
||||||
|
minChars: 1,
|
||||||
|
maxItems: 10,
|
||||||
|
autoFirst: true
|
||||||
|
});
|
||||||
|
|
||||||
|
cityInput.addEventListener('input', function() {
|
||||||
|
const country = countryInput.value;
|
||||||
|
const region = regionInput.value;
|
||||||
|
fetch(`/parks/ajax/cities/?q=${encodeURIComponent(this.value)}&country=${encodeURIComponent(country)}®ion=${encodeURIComponent(region)}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
cityList.list = data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle location link clicks
|
||||||
|
document.querySelectorAll('.location-link').forEach(link => {
|
||||||
|
link.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const params = new URLSearchParams(this.getAttribute('href').split('?')[1]);
|
||||||
|
|
||||||
|
// Update form inputs
|
||||||
|
countryInput.value = params.get('country') || '';
|
||||||
|
regionInput.value = params.get('region') || '';
|
||||||
|
cityInput.value = params.get('city') || '';
|
||||||
|
|
||||||
|
// Trigger form submission
|
||||||
|
htmx.trigger('#park-filters', 'change');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<p class="mb-3 text-gray-600 dark:text-gray-400">
|
<p class="mb-3 text-gray-600 dark:text-gray-400">
|
||||||
<i class="mr-1 fas fa-map-marker-alt"></i>
|
<i class="mr-1 fas fa-map-marker-alt"></i>
|
||||||
{{ park.location }}
|
{{ park.get_formatted_location }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<span class="status-badge {% if park.status == 'OPERATING' %}status-operating
|
<span class="status-badge {% if park.status == 'OPERATING' %}status-operating
|
||||||
|
|||||||
Reference in New Issue
Block a user