mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 11:51:10 -05:00
Update search and login functionality to use new endpoints
Refactor the search component to fetch results from a new endpoint and handle both JSON and HTML responses. Update the authentication modal to utilize Django's allauth for OAuth providers, handling redirects and login success/failure more robustly. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9bc9dd7a-5328-4cb7-91de-b3cb33a0c48c Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
@@ -78,10 +78,30 @@ Alpine.data('searchComponent', () => ({
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/search/?q=${encodeURIComponent(this.query)}`);
|
||||
const data = await response.json();
|
||||
this.results = data.results || [];
|
||||
this.showResults = this.results.length > 0;
|
||||
// Use the same search endpoint as HTMX in the template
|
||||
const response = await fetch(`/search/parks/?q=${encodeURIComponent(this.query)}`, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Try to parse as JSON first, fallback to extracting from HTML
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
const data = await response.json();
|
||||
this.results = data.results || [];
|
||||
} else {
|
||||
// Parse HTML response to extract search results
|
||||
const html = await response.text();
|
||||
this.results = this.parseSearchResults(html);
|
||||
}
|
||||
this.showResults = this.results.length > 0;
|
||||
} else {
|
||||
this.results = [];
|
||||
this.showResults = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
this.results = [];
|
||||
@@ -91,6 +111,36 @@ Alpine.data('searchComponent', () => ({
|
||||
}
|
||||
},
|
||||
|
||||
parseSearchResults(html) {
|
||||
// Helper method to extract search results from HTML response
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const results = [];
|
||||
|
||||
// Look for search result items in the HTML
|
||||
const resultElements = doc.querySelectorAll('[data-search-result], .search-result-item, .park-item');
|
||||
resultElements.forEach(element => {
|
||||
const title = element.querySelector('h3, .title, [data-title]')?.textContent?.trim();
|
||||
const url = element.querySelector('a')?.getAttribute('href');
|
||||
const description = element.querySelector('.description, .excerpt, p')?.textContent?.trim();
|
||||
|
||||
if (title && url) {
|
||||
results.push({
|
||||
title,
|
||||
url,
|
||||
description: description || ''
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return results.slice(0, 10); // Limit to 10 results
|
||||
} catch (error) {
|
||||
console.error('Error parsing search results:', error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
selectResult(result) {
|
||||
window.location.href = result.url;
|
||||
this.showResults = false;
|
||||
@@ -442,16 +492,34 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
login: this.loginForm.username,
|
||||
password: this.loginForm.password
|
||||
})
|
||||
password: this.loginForm.password,
|
||||
next: window.location.pathname
|
||||
}),
|
||||
redirect: 'manual' // Handle redirects manually
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Login successful - reload page to update auth state
|
||||
window.location.reload();
|
||||
// Django allauth returns 302 redirect on successful login
|
||||
if (response.status === 302 || (response.ok && response.status === 200)) {
|
||||
// Check if login was successful by trying to parse response
|
||||
try {
|
||||
const html = await response.text();
|
||||
// If response contains error messages, login failed
|
||||
if (html.includes('errorlist') || html.includes('alert-danger') || html.includes('invalid')) {
|
||||
this.loginError = this.extractErrorFromHTML(html) || 'Login failed. Please check your credentials.';
|
||||
} else {
|
||||
// Login successful - reload page to update auth state
|
||||
window.location.reload();
|
||||
}
|
||||
} catch {
|
||||
// If we can't parse response, assume success and reload
|
||||
window.location.reload();
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
// Form validation errors - parse HTML response for error messages
|
||||
const html = await response.text();
|
||||
this.loginError = this.extractErrorFromHTML(html) || 'Login failed. Please check your credentials.';
|
||||
} else {
|
||||
const data = await response.json();
|
||||
this.loginError = data.message || 'Login failed. Please check your credentials.';
|
||||
this.loginError = 'Login failed. Please check your credentials.';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
@@ -492,17 +560,32 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
||||
username: this.registerForm.username,
|
||||
password1: this.registerForm.password1,
|
||||
password2: this.registerForm.password2
|
||||
})
|
||||
}),
|
||||
redirect: 'manual'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Registration successful
|
||||
this.close();
|
||||
// Show success message or redirect
|
||||
Alpine.store('toast').success('Account created successfully! Please check your email to verify your account.');
|
||||
if (response.status === 302 || response.ok) {
|
||||
try {
|
||||
const html = await response.text();
|
||||
// Check if registration was successful
|
||||
if (html.includes('errorlist') || html.includes('alert-danger') || html.includes('invalid')) {
|
||||
this.registerError = this.extractErrorFromHTML(html) || 'Registration failed. Please try again.';
|
||||
} else {
|
||||
// Registration successful
|
||||
this.close();
|
||||
Alpine.store('toast').success('Account created successfully! Please check your email to verify your account.');
|
||||
}
|
||||
} catch {
|
||||
// Assume success if we can't parse response
|
||||
this.close();
|
||||
Alpine.store('toast').success('Account created successfully! Please check your email to verify your account.');
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
// Form validation errors
|
||||
const html = await response.text();
|
||||
this.registerError = this.extractErrorFromHTML(html) || 'Registration failed. Please try again.';
|
||||
} else {
|
||||
const data = await response.json();
|
||||
this.registerError = data.message || 'Registration failed. Please try again.';
|
||||
this.registerError = 'Registration failed. Please try again.';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Registration error:', error);
|
||||
@@ -523,6 +606,38 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
||||
window.location.href = provider.auth_url;
|
||||
},
|
||||
|
||||
extractErrorFromHTML(html) {
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
// Look for error messages in various formats
|
||||
const errorSelectors = [
|
||||
'.errorlist li',
|
||||
'.alert-danger',
|
||||
'.invalid-feedback',
|
||||
'.form-error',
|
||||
'[class*="error"]',
|
||||
'.field-error'
|
||||
];
|
||||
|
||||
for (const selector of errorSelectors) {
|
||||
const errorElements = doc.querySelectorAll(selector);
|
||||
if (errorElements.length > 0) {
|
||||
return Array.from(errorElements)
|
||||
.map(el => el.textContent.trim())
|
||||
.filter(text => text.length > 0)
|
||||
.join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error parsing HTML for error messages:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getCSRFToken() {
|
||||
const token = document.querySelector('[name=csrfmiddlewaretoken]')?.value ||
|
||||
document.querySelector('meta[name=csrf-token]')?.getAttribute('content') ||
|
||||
|
||||
@@ -51,8 +51,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Update icon
|
||||
const icon = mobileMenuBtn.querySelector('i');
|
||||
icon.classList.remove(isMenuOpen ? 'fa-bars' : 'fa-times');
|
||||
icon.classList.add(isMenuOpen ? 'fa-times' : 'fa-bars');
|
||||
if (icon) {
|
||||
icon.classList.remove(isMenuOpen ? 'fa-bars' : 'fa-times');
|
||||
icon.classList.add(isMenuOpen ? 'fa-times' : 'fa-bars');
|
||||
}
|
||||
};
|
||||
|
||||
mobileMenuBtn.addEventListener('click', toggleMenu);
|
||||
|
||||
Reference in New Issue
Block a user