mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:11:09 -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;
|
this.loading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/search/?q=${encodeURIComponent(this.query)}`);
|
// 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();
|
const data = await response.json();
|
||||||
this.results = data.results || [];
|
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;
|
this.showResults = this.results.length > 0;
|
||||||
|
} else {
|
||||||
|
this.results = [];
|
||||||
|
this.showResults = false;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Search error:', error);
|
console.error('Search error:', error);
|
||||||
this.results = [];
|
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) {
|
selectResult(result) {
|
||||||
window.location.href = result.url;
|
window.location.href = result.url;
|
||||||
this.showResults = false;
|
this.showResults = false;
|
||||||
@@ -442,16 +492,34 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
|||||||
},
|
},
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
login: this.loginForm.username,
|
login: this.loginForm.username,
|
||||||
password: this.loginForm.password
|
password: this.loginForm.password,
|
||||||
})
|
next: window.location.pathname
|
||||||
|
}),
|
||||||
|
redirect: 'manual' // Handle redirects manually
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
// 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
|
// Login successful - reload page to update auth state
|
||||||
window.location.reload();
|
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 {
|
} else {
|
||||||
const data = await response.json();
|
this.loginError = 'Login failed. Please check your credentials.';
|
||||||
this.loginError = data.message || 'Login failed. Please check your credentials.';
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login error:', error);
|
console.error('Login error:', error);
|
||||||
@@ -492,17 +560,32 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
|||||||
username: this.registerForm.username,
|
username: this.registerForm.username,
|
||||||
password1: this.registerForm.password1,
|
password1: this.registerForm.password1,
|
||||||
password2: this.registerForm.password2
|
password2: this.registerForm.password2
|
||||||
})
|
}),
|
||||||
|
redirect: 'manual'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
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
|
// Registration successful
|
||||||
this.close();
|
this.close();
|
||||||
// Show success message or redirect
|
|
||||||
Alpine.store('toast').success('Account created successfully! Please check your email to verify your account.');
|
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 {
|
} else {
|
||||||
const data = await response.json();
|
this.registerError = 'Registration failed. Please try again.';
|
||||||
this.registerError = data.message || 'Registration failed. Please try again.';
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Registration error:', error);
|
console.error('Registration error:', error);
|
||||||
@@ -523,6 +606,38 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
|
|||||||
window.location.href = provider.auth_url;
|
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() {
|
getCSRFToken() {
|
||||||
const token = document.querySelector('[name=csrfmiddlewaretoken]')?.value ||
|
const token = document.querySelector('[name=csrfmiddlewaretoken]')?.value ||
|
||||||
document.querySelector('meta[name=csrf-token]')?.getAttribute('content') ||
|
document.querySelector('meta[name=csrf-token]')?.getAttribute('content') ||
|
||||||
|
|||||||
@@ -51,8 +51,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Update icon
|
// Update icon
|
||||||
const icon = mobileMenuBtn.querySelector('i');
|
const icon = mobileMenuBtn.querySelector('i');
|
||||||
|
if (icon) {
|
||||||
icon.classList.remove(isMenuOpen ? 'fa-bars' : 'fa-times');
|
icon.classList.remove(isMenuOpen ? 'fa-bars' : 'fa-times');
|
||||||
icon.classList.add(isMenuOpen ? 'fa-times' : 'fa-bars');
|
icon.classList.add(isMenuOpen ? 'fa-times' : 'fa-bars');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mobileMenuBtn.addEventListener('click', toggleMenu);
|
mobileMenuBtn.addEventListener('click', toggleMenu);
|
||||||
|
|||||||
Reference in New Issue
Block a user