Implement RideUpdateView and refactor search suggestion handling for improved modularity and error management

This commit is contained in:
pacnpal
2025-02-10 23:35:58 -05:00
parent fb6c6ec37b
commit 6034227796
2 changed files with 91 additions and 64 deletions

View File

@@ -1,4 +1,17 @@
# Keep all imports and previous classes up to RideCreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.views.generic import UpdateView
from companies.models import Manufacturer
from designers.models import Designer
from moderation.mixins import EditSubmissionMixin
from apps.parks.mixins.base import ParkContextRequired # type: ignore
from moderation.models import EditSubmission
from parks.models import Park
from rides.forms import RideForm # type: ignore
from .models import Ride, RideModel
class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixin, UpdateView):
"""View for updating an existing ride"""

View File

@@ -116,6 +116,19 @@ document.addEventListener('alpine:init', () => {
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5s timeout
try {
const response = await this.fetchSuggestions(controller, requestId);
await this.handleSuggestionResponse(response, requestId);
} catch (error) {
this.handleSuggestionError(error, requestId);
} finally {
clearTimeout(timeoutId);
if (this.currentRequest === controller) {
this.currentRequest = null;
}
}
},
async fetchSuggestions(controller, requestId) {
const parkSlug = document.querySelector('input[name="park_slug"]')?.value;
const url = `/rides/search-suggestions/?q=${encodeURIComponent(this.searchQuery)}${parkSlug ? '&park_slug=' + parkSlug : ''}`;
@@ -129,16 +142,22 @@ document.addEventListener('alpine:init', () => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response;
},
async handleSuggestionResponse(response, requestId) {
const html = await response.text();
// Only update if this is still the most recent request
if (requestId === this.lastRequestId && this.searchQuery === document.getElementById('search').value) {
const suggestionsEl = document.getElementById('search-suggestions');
suggestionsEl.innerHTML = html;
this.showSuggestions = html.trim() ? true : false;
this.showSuggestions = Boolean(html.trim());
// Set proper ARIA attributes
this.updateAriaAttributes(suggestionsEl);
}
},
updateAriaAttributes(suggestionsEl) {
const searchInput = document.getElementById('search');
searchInput.setAttribute('aria-expanded', this.showSuggestions.toString());
searchInput.setAttribute('aria-controls', 'search-suggestions');
@@ -148,11 +167,14 @@ document.addEventListener('alpine:init', () => {
btn.setAttribute('role', 'option');
});
}
}
} catch (error) {
},
handleSuggestionError(error, requestId) {
if (error.name === 'AbortError') {
console.warn('Search suggestion request timed out or cancelled');
} else {
return;
}
console.error('Error fetching suggestions:', error);
if (requestId === this.lastRequestId) {
const suggestionsEl = document.getElementById('search-suggestions');
@@ -162,13 +184,6 @@ document.addEventListener('alpine:init', () => {
</div>`;
this.showSuggestions = true;
}
}
} finally {
clearTimeout(timeoutId);
if (this.currentRequest === controller) {
this.currentRequest = null;
}
}
},
// Handle input changes with debounce
@@ -277,24 +292,6 @@ document.addEventListener('alpine:init', () => {
}
}));
});
</script>
<!-- HTMX Loading Indicator Styles -->
<style>
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1
}
/* Enhanced Loading Indicator */
.loading-indicator {
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 50;
},
performCleanup() {
@@ -324,6 +321,23 @@ document.addEventListener('alpine:init', () => {
}));
});
</script>
<!-- HTMX Loading Indicator Styles -->
<style>
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1
}
/* Enhanced Loading Indicator */
.loading-indicator {
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 50;
display: flex;
align-items: center;
gap: 0.5rem;