mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:31:09 -05:00
here we go
This commit is contained in:
18
static/js/alerts.js
Normal file
18
static/js/alerts.js
Normal file
@@ -0,0 +1,18 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Get all alert elements
|
||||
const alerts = document.querySelectorAll('.alert');
|
||||
|
||||
// For each alert
|
||||
alerts.forEach(alert => {
|
||||
// After 5 seconds
|
||||
setTimeout(() => {
|
||||
// Add slideOut animation
|
||||
alert.style.animation = 'slideOut 0.5s ease-out forwards';
|
||||
|
||||
// Remove the alert after animation completes
|
||||
setTimeout(() => {
|
||||
alert.remove();
|
||||
}, 500);
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
5
static/js/alpine.min.js
vendored
Normal file
5
static/js/alpine.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,262 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Handle edit button clicks
|
||||
document.querySelectorAll('[data-edit-button]').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const contentId = this.dataset.contentId;
|
||||
const contentType = this.dataset.contentType;
|
||||
const editableFields = document.querySelectorAll(`[data-editable][data-content-id="${contentId}"]`);
|
||||
|
||||
// Toggle edit mode
|
||||
editableFields.forEach(field => {
|
||||
const currentValue = field.textContent.trim();
|
||||
const fieldName = field.dataset.fieldName;
|
||||
const fieldType = field.dataset.fieldType || 'text';
|
||||
|
||||
// Create input field
|
||||
let input;
|
||||
if (fieldType === 'textarea') {
|
||||
input = document.createElement('textarea');
|
||||
input.value = currentValue;
|
||||
input.rows = 4;
|
||||
} else if (fieldType === 'select') {
|
||||
input = document.createElement('select');
|
||||
// Get options from data attribute
|
||||
const options = JSON.parse(field.dataset.options || '[]');
|
||||
options.forEach(option => {
|
||||
const optionEl = document.createElement('option');
|
||||
optionEl.value = option.value;
|
||||
optionEl.textContent = option.label;
|
||||
optionEl.selected = option.value === currentValue;
|
||||
input.appendChild(optionEl);
|
||||
});
|
||||
} else if (fieldType === 'date') {
|
||||
input = document.createElement('input');
|
||||
input.type = 'date';
|
||||
input.value = currentValue;
|
||||
} else if (fieldType === 'number') {
|
||||
input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.value = currentValue;
|
||||
if (field.dataset.min) input.min = field.dataset.min;
|
||||
if (field.dataset.max) input.max = field.dataset.max;
|
||||
if (field.dataset.step) input.step = field.dataset.step;
|
||||
} else {
|
||||
input = document.createElement('input');
|
||||
input.type = fieldType;
|
||||
input.value = currentValue;
|
||||
}
|
||||
|
||||
input.className = 'w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white';
|
||||
input.dataset.originalValue = currentValue;
|
||||
input.dataset.fieldName = fieldName;
|
||||
|
||||
// Replace content with input
|
||||
field.textContent = '';
|
||||
field.appendChild(input);
|
||||
});
|
||||
|
||||
// Show save/cancel buttons
|
||||
const actionButtons = document.createElement('div');
|
||||
actionButtons.className = 'flex gap-2 mt-2';
|
||||
actionButtons.innerHTML = `
|
||||
<button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600" data-save-button>
|
||||
<i class="mr-2 fas fa-save"></i>Save Changes
|
||||
</button>
|
||||
<button class="px-4 py-2 text-gray-700 bg-gray-200 rounded-lg hover:bg-gray-300 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500" data-cancel-button>
|
||||
<i class="mr-2 fas fa-times"></i>Cancel
|
||||
</button>
|
||||
${this.dataset.requireReason ? `
|
||||
<div class="flex-grow">
|
||||
<input type="text" class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
placeholder="Reason for changes (required)"
|
||||
data-reason-input>
|
||||
<input type="text" class="w-full mt-1 border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
placeholder="Source (optional)"
|
||||
data-source-input>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
const container = editableFields[0].closest('.editable-container');
|
||||
container.appendChild(actionButtons);
|
||||
|
||||
// Hide edit button while editing
|
||||
this.style.display = 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Handle form submissions
|
||||
document.querySelectorAll('form[data-submit-type]').forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const submitType = this.dataset.submitType;
|
||||
const formData = new FormData(this);
|
||||
const data = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
data[key] = value;
|
||||
});
|
||||
|
||||
// Get CSRF token from meta tag
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
|
||||
// Submit form
|
||||
fetch(this.action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
submission_type: submitType,
|
||||
...data
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
showNotification(data.message, 'success');
|
||||
if (data.redirect_url) {
|
||||
window.location.href = data.redirect_url;
|
||||
}
|
||||
} else {
|
||||
showNotification(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('An error occurred while submitting the form.', 'error');
|
||||
console.error('Error:', error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Handle save button clicks using event delegation
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.matches('[data-save-button]')) {
|
||||
const container = e.target.closest('.editable-container');
|
||||
const contentId = container.querySelector('[data-editable]').dataset.contentId;
|
||||
const contentType = container.querySelector('[data-edit-button]').dataset.contentType;
|
||||
const editableFields = container.querySelectorAll('[data-editable]');
|
||||
|
||||
// Collect changes
|
||||
const changes = {};
|
||||
editableFields.forEach(field => {
|
||||
const input = field.querySelector('input, textarea, select');
|
||||
if (input && input.value !== input.dataset.originalValue) {
|
||||
changes[input.dataset.fieldName] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
// If no changes, just cancel
|
||||
if (Object.keys(changes).length === 0) {
|
||||
cancelEdit(container);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get reason and source if required
|
||||
const reasonInput = container.querySelector('[data-reason-input]');
|
||||
const sourceInput = container.querySelector('[data-source-input]');
|
||||
const reason = reasonInput ? reasonInput.value : '';
|
||||
const source = sourceInput ? sourceInput.value : '';
|
||||
|
||||
// Validate reason if required
|
||||
if (reasonInput && !reason) {
|
||||
alert('Please provide a reason for your changes.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get CSRF token from meta tag
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
|
||||
// Submit changes
|
||||
fetch(window.location.pathname, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_type: contentType,
|
||||
content_id: contentId,
|
||||
changes,
|
||||
reason,
|
||||
source
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
if (data.auto_approved) {
|
||||
// Update the display immediately
|
||||
Object.entries(changes).forEach(([field, value]) => {
|
||||
const element = container.querySelector(`[data-editable][data-field-name="${field}"]`);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
showNotification(data.message, 'success');
|
||||
if (data.redirect_url) {
|
||||
window.location.href = data.redirect_url;
|
||||
}
|
||||
} else {
|
||||
showNotification(data.message, 'error');
|
||||
}
|
||||
cancelEdit(container);
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('An error occurred while saving changes.', 'error');
|
||||
console.error('Error:', error);
|
||||
cancelEdit(container);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle cancel button clicks using event delegation
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.matches('[data-cancel-button]')) {
|
||||
const container = e.target.closest('.editable-container');
|
||||
cancelEdit(container);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function cancelEdit(container) {
|
||||
// Restore original content
|
||||
container.querySelectorAll('[data-editable]').forEach(field => {
|
||||
const input = field.querySelector('input, textarea, select');
|
||||
if (input) {
|
||||
field.textContent = input.dataset.originalValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove action buttons
|
||||
const actionButtons = container.querySelector('.flex.gap-2');
|
||||
if (actionButtons) {
|
||||
actionButtons.remove();
|
||||
}
|
||||
|
||||
// Show edit button
|
||||
const editButton = container.querySelector('[data-edit-button]');
|
||||
if (editButton) {
|
||||
editButton.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `fixed bottom-4 right-4 p-4 rounded-lg shadow-lg text-white ${
|
||||
type === 'success' ? 'bg-green-600 dark:bg-green-500' :
|
||||
type === 'error' ? 'bg-red-600 dark:bg-red-500' :
|
||||
'bg-blue-600 dark:bg-blue-500'
|
||||
}`;
|
||||
notification.textContent = message;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 5000);
|
||||
}
|
||||
81
static/js/location-autocomplete.js
Normal file
81
static/js/location-autocomplete.js
Normal file
@@ -0,0 +1,81 @@
|
||||
function locationAutocomplete(field, filterParks = false) {
|
||||
return {
|
||||
query: '',
|
||||
suggestions: [],
|
||||
fetchSuggestions() {
|
||||
let url;
|
||||
const params = new URLSearchParams({
|
||||
q: this.query,
|
||||
filter_parks: filterParks
|
||||
});
|
||||
|
||||
switch (field) {
|
||||
case 'country':
|
||||
url = '/parks/ajax/countries/';
|
||||
break;
|
||||
case 'region':
|
||||
url = '/parks/ajax/regions/';
|
||||
// Add country parameter if we're fetching regions
|
||||
const countryInput = document.getElementById(filterParks ? 'country' : 'id_country_name');
|
||||
if (countryInput && countryInput.value) {
|
||||
params.append('country', countryInput.value);
|
||||
}
|
||||
break;
|
||||
case 'city':
|
||||
url = '/parks/ajax/cities/';
|
||||
// Add country and region parameters if we're fetching cities
|
||||
const regionInput = document.getElementById(filterParks ? 'region' : 'id_region_name');
|
||||
const cityCountryInput = document.getElementById(filterParks ? 'country' : 'id_country_name');
|
||||
if (regionInput && regionInput.value && cityCountryInput && cityCountryInput.value) {
|
||||
params.append('country', cityCountryInput.value);
|
||||
params.append('region', regionInput.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
fetch(`${url}?${params}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
this.suggestions = data;
|
||||
});
|
||||
}
|
||||
},
|
||||
selectSuggestion(suggestion) {
|
||||
this.query = suggestion.name;
|
||||
this.suggestions = [];
|
||||
|
||||
// If this is a form field (not filter), update hidden fields
|
||||
if (!filterParks) {
|
||||
const hiddenField = document.getElementById(`id_${field}`);
|
||||
if (hiddenField) {
|
||||
hiddenField.value = suggestion.id;
|
||||
}
|
||||
|
||||
// Clear dependent fields when parent field changes
|
||||
if (field === 'country') {
|
||||
const regionInput = document.getElementById('id_region_name');
|
||||
const cityInput = document.getElementById('id_city_name');
|
||||
const regionHidden = document.getElementById('id_region');
|
||||
const cityHidden = document.getElementById('id_city');
|
||||
|
||||
if (regionInput) regionInput.value = '';
|
||||
if (cityInput) cityInput.value = '';
|
||||
if (regionHidden) regionHidden.value = '';
|
||||
if (cityHidden) cityHidden.value = '';
|
||||
} else if (field === 'region') {
|
||||
const cityInput = document.getElementById('id_city_name');
|
||||
const cityHidden = document.getElementById('id_city');
|
||||
|
||||
if (cityInput) cityInput.value = '';
|
||||
if (cityHidden) cityHidden.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger form submission for filters
|
||||
if (filterParks) {
|
||||
htmx.trigger('#park-filters', 'change');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user