mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
1178 lines
43 KiB
JavaScript
1178 lines
43 KiB
JavaScript
// Template Editor JavaScript
|
|
|
|
// Template Reset Component function for Alpine.js (define before DOM ready)
|
|
window.templateResetComponent = function() {
|
|
return {
|
|
resetOpen: false,
|
|
resetConfirm: false,
|
|
resetAnalysis: null,
|
|
loading: false,
|
|
confirmText: '',
|
|
|
|
toggleReset() {
|
|
this.resetOpen = !this.resetOpen;
|
|
if (this.resetOpen && !this.resetAnalysis) {
|
|
this.fetchAnalysis();
|
|
}
|
|
},
|
|
|
|
fetchAnalysis() {
|
|
console.log('Fetching reset analysis...');
|
|
this.loading = true;
|
|
const contentType = document.querySelector('.attributes-editor').dataset.contentType;
|
|
|
|
fetch(`/plan/${contentType}/template/reset`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Analysis data:', data);
|
|
this.resetAnalysis = data;
|
|
this.loading = false;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
this.loading = false;
|
|
if (typeof showNotification === 'function') {
|
|
showNotification('Failed to analyze template reset impact', 'error');
|
|
} else {
|
|
alert('Failed to analyze template reset impact');
|
|
}
|
|
});
|
|
},
|
|
|
|
performReset() {
|
|
console.log('Performing reset...');
|
|
this.loading = true;
|
|
const contentType = document.querySelector('.attributes-editor').dataset.contentType;
|
|
|
|
fetch(`/plan/${contentType}/template/reset?confirm=true`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
if (typeof showNotification === 'function') {
|
|
showNotification(data.message, 'success');
|
|
} else {
|
|
alert(data.message);
|
|
}
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else {
|
|
if (typeof showNotification === 'function') {
|
|
showNotification(data.error || 'Failed to reset template', 'error');
|
|
} else {
|
|
alert(data.error || 'Failed to reset template');
|
|
}
|
|
this.loading = false;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
if (typeof showNotification === 'function') {
|
|
showNotification('Failed to reset template', 'error');
|
|
} else {
|
|
alert('Failed to reset template');
|
|
}
|
|
this.loading = false;
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
// Initialize Alpine component data (global function)
|
|
window.initTemplateEditor = function() {
|
|
return {
|
|
selectedCategory: null,
|
|
selectedField: null,
|
|
configuring: false,
|
|
activePanel: window.innerWidth >= 768 ? 'both' : 'template',
|
|
|
|
// Initialize reset analysis as empty object to prevent Alpine errors
|
|
resetAnalysis: null,
|
|
|
|
// Category selection
|
|
selectCategory(categoryId) {
|
|
this.selectedCategory = categoryId;
|
|
this.selectedField = null;
|
|
this.configuring = true;
|
|
|
|
if (window.innerWidth < 768) {
|
|
this.activePanel = 'config';
|
|
}
|
|
|
|
// Show loading animation
|
|
showConfigLoadingAnimation('category-config-container');
|
|
|
|
// Load category configuration via AJAX
|
|
fetch(`/plan/attribute_categories/${categoryId}/edit`)
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
document.getElementById('category-config-container').innerHTML = html;
|
|
// Bind remote form handlers to newly loaded forms
|
|
bindRemoteFormsInContainer('category-config-container');
|
|
// Hide loading animation
|
|
hideConfigLoadingAnimation('category-config-container');
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading category config:', error);
|
|
hideConfigLoadingAnimation('category-config-container');
|
|
showNotification('Failed to load category configuration', 'error');
|
|
});
|
|
},
|
|
|
|
// Field selection
|
|
selectField(fieldId) {
|
|
this.selectedField = fieldId;
|
|
this.selectedCategory = null;
|
|
this.configuring = true;
|
|
|
|
if (window.innerWidth < 768) {
|
|
this.activePanel = 'config';
|
|
}
|
|
|
|
// Show loading animation
|
|
showConfigLoadingAnimation('field-config-container');
|
|
|
|
// Load field configuration via AJAX
|
|
fetch(`/plan/attribute_fields/${fieldId}/edit`)
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
document.getElementById('field-config-container').innerHTML = html;
|
|
// Bind remote form handlers to newly loaded forms
|
|
bindRemoteFormsInContainer('field-config-container');
|
|
// Hide loading animation
|
|
hideConfigLoadingAnimation('field-config-container');
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading field config:', error);
|
|
hideConfigLoadingAnimation('field-config-container');
|
|
showNotification('Failed to load field configuration', 'error');
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
// Initialize sortable for categories
|
|
initSortables();
|
|
|
|
// Handle remote form submissions for dynamically loaded forms
|
|
setupRemoteFormHandlers();
|
|
|
|
// Show category form
|
|
document.addEventListener('click', function(event) {
|
|
if (event.target.closest('[data-action="click->attributes-editor#showAddCategoryForm"]')) {
|
|
const form = document.getElementById('add-category-form');
|
|
form.classList.toggle('hidden');
|
|
}
|
|
});
|
|
|
|
// Handle select-category event dispatched from category cards
|
|
document.addEventListener('select-category', function(event) {
|
|
const alpineElement = document.querySelector('.attributes-editor');
|
|
if (alpineElement && alpineElement._x_dataStack) {
|
|
const alpine = alpineElement._x_dataStack[0];
|
|
if (alpine.selectCategory) {
|
|
alpine.selectCategory(event.detail.id);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Handle select-field event dispatched from field items
|
|
document.addEventListener('select-field', function(event) {
|
|
const alpineElement = document.querySelector('.attributes-editor');
|
|
if (alpineElement && alpineElement._x_dataStack) {
|
|
const alpine = alpineElement._x_dataStack[0];
|
|
if (alpine.selectField) {
|
|
alpine.selectField(event.detail.id);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Category suggestions
|
|
document.querySelectorAll('.js-show-category-suggestions').forEach(button => {
|
|
button.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const contentType = document.querySelector('.attributes-editor').dataset.contentType;
|
|
const resultContainer = this.closest('div').querySelector('.suggest-categories-container');
|
|
|
|
// Show loading animation in the suggestions container
|
|
showSuggestionsLoadingAnimation(resultContainer);
|
|
|
|
fetch(`/plan/attribute_categories/suggest?content_type=${contentType}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const existingCategories = Array.from(document.querySelectorAll('.category-label')).map(el => el.textContent.trim());
|
|
const newCategories = data.filter(c => !existingCategories.includes(c));
|
|
|
|
if (newCategories.length > 0) {
|
|
resultContainer.innerHTML = '';
|
|
newCategories.forEach(category => {
|
|
const chip = document.createElement('span');
|
|
chip.className = 'category-suggestion-link px-3 py-1 bg-gray-100 text-sm text-gray-800 rounded-full hover:bg-gray-200 cursor-pointer';
|
|
chip.textContent = category;
|
|
chip.addEventListener('click', function() {
|
|
document.querySelector('.js-category-input').value = category;
|
|
});
|
|
resultContainer.appendChild(chip);
|
|
});
|
|
} else {
|
|
resultContainer.innerHTML = '<p class="text-sm text-gray-500">No suggestions available at the moment.</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading category suggestions:', error);
|
|
resultContainer.innerHTML = '<p class="text-sm text-red-500">Failed to load suggestions. Please try again.</p>';
|
|
});
|
|
|
|
this.style.display = 'none';
|
|
});
|
|
});
|
|
|
|
// Field suggestions
|
|
document.querySelectorAll('.js-show-field-suggestions').forEach(button => {
|
|
button.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const contentType = document.querySelector('.attributes-editor').dataset.contentType;
|
|
const categoryContainer = this.closest('li') || this.closest('.category-card');
|
|
const categoryLabel = categoryContainer.querySelector('.category-label').textContent.trim();
|
|
const resultContainer = this.closest('div').querySelector('.suggest-fields-container');
|
|
|
|
// Show loading animation in the suggestions container
|
|
showSuggestionsLoadingAnimation(resultContainer);
|
|
|
|
fetch(`/plan/attribute_fields/suggest?content_type=${contentType}&category=${categoryLabel}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const existingFields = Array.from(categoryContainer.querySelectorAll('.field-label')).map(el => el.textContent.trim());
|
|
const newFields = data.filter(f => !existingFields.includes(f));
|
|
|
|
if (newFields.length > 0) {
|
|
resultContainer.innerHTML = '';
|
|
newFields.forEach(field => {
|
|
const chip = document.createElement('span');
|
|
chip.className = 'field-suggestion-link px-3 py-1 bg-gray-100 text-sm text-gray-800 rounded-full hover:bg-gray-200 cursor-pointer';
|
|
chip.textContent = field;
|
|
chip.addEventListener('click', function() {
|
|
categoryContainer.querySelector('.js-field-input').value = field;
|
|
});
|
|
resultContainer.appendChild(chip);
|
|
});
|
|
} else {
|
|
resultContainer.innerHTML = '<p class="text-sm text-gray-500">No suggestions available at the moment.</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading field suggestions:', error);
|
|
resultContainer.innerHTML = '<p class="text-sm text-red-500">Failed to load suggestions. Please try again.</p>';
|
|
});
|
|
|
|
this.style.display = 'none';
|
|
});
|
|
});
|
|
});
|
|
|
|
// Initialize sortable functionality using jQuery UI
|
|
function initSortables() {
|
|
if (typeof $ === 'undefined' || !$.fn.sortable) {
|
|
console.error('jQuery UI Sortable not found');
|
|
return;
|
|
}
|
|
|
|
// Categories sorting
|
|
$('#categories-container').sortable({
|
|
items: '.category-card',
|
|
handle: '.category-drag-handle',
|
|
placeholder: 'category-placeholder',
|
|
cursor: 'move',
|
|
opacity: 0.8,
|
|
tolerance: 'pointer',
|
|
update: function(event, ui) {
|
|
const categoryId = ui.item.attr('data-category-id');
|
|
const newPosition = ui.item.index();
|
|
|
|
// AJAX request to update position using internal endpoint
|
|
$.ajax({
|
|
url: '/internal/sort/categories',
|
|
type: 'PATCH',
|
|
contentType: 'application/json',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
data: JSON.stringify({
|
|
content_id: categoryId,
|
|
intended_position: newPosition
|
|
}),
|
|
success: function(data) {
|
|
console.log('Category position updated successfully:', data);
|
|
if (data.message) {
|
|
showNotification(data.message, 'success');
|
|
}
|
|
|
|
// Update the position field in the category config form if it's open
|
|
updateCategoryConfigPosition(categoryId, data.category.position);
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('Error updating category position:', error);
|
|
showErrorMessage('Failed to reorder categories. Please try again.');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Fields sorting for each category
|
|
$('.fields-container').sortable({
|
|
items: '.field-item',
|
|
handle: '.field-drag-handle',
|
|
placeholder: 'field-placeholder',
|
|
cursor: 'move',
|
|
opacity: 0.8,
|
|
tolerance: 'pointer',
|
|
connectWith: '.fields-container',
|
|
update: function(event, ui) {
|
|
const fieldId = ui.item.attr('data-field-id');
|
|
const newPosition = ui.item.index();
|
|
const categoryId = ui.item.closest('.fields-container').attr('data-category-id');
|
|
|
|
// AJAX request to update position using internal endpoint
|
|
$.ajax({
|
|
url: '/internal/sort/fields',
|
|
type: 'PATCH',
|
|
contentType: 'application/json',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
data: JSON.stringify({
|
|
content_id: fieldId,
|
|
intended_position: newPosition,
|
|
attribute_category_id: categoryId
|
|
}),
|
|
success: function(data) {
|
|
console.log('Field position updated successfully:', data);
|
|
if (data.message) {
|
|
showNotification(data.message, 'success');
|
|
}
|
|
|
|
// Update the position field in the field config form if it's open
|
|
updateFieldConfigPosition(fieldId, data.field.position);
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('Error updating field position:', error);
|
|
showErrorMessage('Failed to reorder fields. Please try again.');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Notification system
|
|
function showNotification(message, type = 'info') {
|
|
const bgColor = type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-blue-500';
|
|
const icon = type === 'success' ? 'check_circle' : type === 'error' ? 'error' : 'info';
|
|
|
|
const notification = $(`
|
|
<div class="fixed top-4 right-4 ${bgColor} text-white px-6 py-3 rounded-lg shadow-lg z-50 flex items-center animate-notification-in">
|
|
<i class="material-icons text-sm mr-2">${icon}</i>
|
|
<span class="text-sm font-medium">${message}</span>
|
|
<button class="ml-4 text-white hover:text-gray-200" onclick="$(this).parent().remove()">
|
|
<i class="material-icons text-sm">close</i>
|
|
</button>
|
|
</div>
|
|
`);
|
|
|
|
$('body').append(notification);
|
|
|
|
// Auto-remove after 5 seconds
|
|
setTimeout(() => {
|
|
notification.fadeOut(300, function() {
|
|
$(this).remove();
|
|
});
|
|
}, 5000);
|
|
}
|
|
|
|
// Make showNotification globally available
|
|
window.showNotification = showNotification;
|
|
|
|
// Category visibility toggle function
|
|
window.toggleCategoryVisibility = function(categoryId, isHidden) {
|
|
const categoryCard = document.querySelector(`[data-category-id="${categoryId}"]`);
|
|
const button = categoryCard.querySelector('.category-visibility-toggle');
|
|
|
|
fetch(`/plan/attribute_categories/${categoryId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
attribute_category: {
|
|
hidden: !isHidden
|
|
}
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success || data.message) {
|
|
// Update the UI manually instead of reloading
|
|
const newHiddenState = !isHidden;
|
|
|
|
// Update button data attribute and title
|
|
button.setAttribute('data-hidden', newHiddenState);
|
|
button.setAttribute('title', newHiddenState ? 'Hidden category - Click to show' : 'Visible category - Click to hide');
|
|
|
|
// Update the eye icon
|
|
const eyeIcon = button.querySelector('svg');
|
|
if (newHiddenState) {
|
|
// Show closed eye icon
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>';
|
|
} else {
|
|
// Show open eye icon
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>';
|
|
}
|
|
|
|
// Update category card styling
|
|
if (newHiddenState) {
|
|
categoryCard.classList.add('border-gray-300');
|
|
categoryCard.style.borderColor = '';
|
|
categoryCard.querySelector('.category-header').style.backgroundColor = '#f9fafb';
|
|
categoryCard.querySelector('.category-icon i').classList.add('text-gray-400');
|
|
categoryCard.querySelector('.category-icon i').style.color = '';
|
|
} else {
|
|
categoryCard.classList.remove('border-gray-300');
|
|
const contentTypeColor = getComputedStyle(document.documentElement).getPropertyValue('--content-type-color') || '#6366f1';
|
|
categoryCard.style.borderColor = contentTypeColor;
|
|
categoryCard.querySelector('.category-header').style.backgroundColor = contentTypeColor + '20';
|
|
categoryCard.querySelector('.category-icon i').classList.remove('text-gray-400');
|
|
categoryCard.querySelector('.category-icon i').style.color = contentTypeColor;
|
|
}
|
|
|
|
// Update hidden status text
|
|
const statusText = categoryCard.querySelector('.category-label').parentElement.querySelector('p');
|
|
if (newHiddenState) {
|
|
if (!statusText.textContent.includes('— Hidden')) {
|
|
statusText.innerHTML += '<span class="text-gray-400 ml-2">— Hidden</span>';
|
|
}
|
|
} else {
|
|
statusText.innerHTML = statusText.innerHTML.replace('<span class="text-gray-400 ml-2">— Hidden</span>', '');
|
|
}
|
|
|
|
showNotification(data.message, 'success');
|
|
} else {
|
|
showNotification('Failed to update category visibility', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error toggling category visibility:', error);
|
|
showNotification('Failed to update category visibility', 'error');
|
|
});
|
|
};
|
|
|
|
// Field visibility toggle function
|
|
window.toggleFieldVisibility = function(fieldId, isHidden) {
|
|
const fieldItem = document.querySelector(`[data-field-id="${fieldId}"]`);
|
|
const button = fieldItem.querySelector('.field-visibility-toggle');
|
|
|
|
fetch(`/plan/attribute_fields/${fieldId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
attribute_field: {
|
|
hidden: !isHidden
|
|
}
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success || data.message) {
|
|
// Update the UI manually instead of reloading
|
|
const newHiddenState = !isHidden;
|
|
|
|
// Update button data attribute and title
|
|
button.setAttribute('data-hidden', newHiddenState);
|
|
button.setAttribute('title', newHiddenState ? 'Hidden field - Click to show' : 'Visible field - Click to hide');
|
|
|
|
// Update the eye icon
|
|
const eyeIcon = button.querySelector('svg');
|
|
if (newHiddenState) {
|
|
// Show closed eye icon
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>';
|
|
} else {
|
|
// Show open eye icon
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>';
|
|
}
|
|
|
|
// Update field item styling
|
|
if (newHiddenState) {
|
|
fieldItem.classList.add('bg-gray-50', 'border-gray-200');
|
|
fieldItem.classList.remove('bg-white');
|
|
fieldItem.querySelector('.field-label').classList.add('text-gray-500');
|
|
fieldItem.querySelector('.field-label').classList.remove('text-gray-800');
|
|
} else {
|
|
fieldItem.classList.remove('bg-gray-50', 'border-gray-200');
|
|
fieldItem.classList.add('bg-white');
|
|
fieldItem.querySelector('.field-label').classList.remove('text-gray-500');
|
|
fieldItem.querySelector('.field-label').classList.add('text-gray-800');
|
|
}
|
|
|
|
// Update hidden status text in field info
|
|
const fieldInfo = fieldItem.querySelector('.text-xs.text-gray-500');
|
|
if (newHiddenState) {
|
|
if (!fieldInfo.textContent.includes('— Hidden')) {
|
|
fieldInfo.innerHTML += '<span class="ml-1.5 text-gray-400">— Hidden</span>';
|
|
}
|
|
} else {
|
|
fieldInfo.innerHTML = fieldInfo.innerHTML.replace('<span class="ml-1.5 text-gray-400">— Hidden</span>', '');
|
|
}
|
|
|
|
showNotification(data.message, 'success');
|
|
} else {
|
|
showNotification('Failed to update field visibility', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error toggling field visibility:', error);
|
|
showNotification('Failed to update field visibility', 'error');
|
|
});
|
|
};
|
|
|
|
// Category icon preview function
|
|
window.updateCategoryIconPreview = function(categoryId, iconName) {
|
|
// Update the icon preview in the category header
|
|
const categoryCard = document.querySelector(`[data-category-id="${categoryId}"]`);
|
|
if (categoryCard) {
|
|
const iconElement = categoryCard.querySelector('.category-icon i');
|
|
if (iconElement) {
|
|
iconElement.textContent = iconName;
|
|
}
|
|
}
|
|
|
|
// Update the form to show the selected icon
|
|
const iconPreview = document.getElementById('selected-icon-preview');
|
|
if (iconPreview) {
|
|
iconPreview.textContent = iconName;
|
|
}
|
|
};
|
|
|
|
// Function to show error messages to users (legacy compatibility)
|
|
function showErrorMessage(message) {
|
|
showNotification(message, 'error');
|
|
}
|
|
|
|
// Bind remote form handlers to dynamically loaded forms in a container
|
|
function bindRemoteFormsInContainer(containerId) {
|
|
const container = document.getElementById(containerId);
|
|
if (!container) return;
|
|
|
|
// Find all forms with remote: true in the container
|
|
const remoteForms = container.querySelectorAll('form[data-remote="true"]');
|
|
|
|
remoteForms.forEach(form => {
|
|
// Remove any existing event listeners to prevent duplicates
|
|
form.removeEventListener('submit', handleRemoteFormSubmit);
|
|
|
|
// Add our custom submit handler
|
|
form.addEventListener('submit', handleRemoteFormSubmit);
|
|
});
|
|
}
|
|
|
|
// Handle remote form submission manually
|
|
function handleRemoteFormSubmit(event) {
|
|
event.preventDefault(); // Prevent default form submission
|
|
|
|
const form = event.target;
|
|
const formData = new FormData(form);
|
|
const submitButton = form.querySelector('input[type="submit"], button[type="submit"]');
|
|
let originalText = '';
|
|
|
|
// Disable submit button to prevent double submission
|
|
if (submitButton) {
|
|
submitButton.disabled = true;
|
|
originalText = submitButton.value || submitButton.textContent;
|
|
if (submitButton.tagName === 'INPUT') {
|
|
submitButton.value = 'Saving...';
|
|
} else {
|
|
submitButton.textContent = 'Saving...';
|
|
}
|
|
|
|
// Restore button after 3 seconds as fallback
|
|
setTimeout(() => {
|
|
submitButton.disabled = false;
|
|
if (submitButton.tagName === 'INPUT') {
|
|
submitButton.value = originalText;
|
|
} else {
|
|
submitButton.textContent = originalText;
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
// Convert FormData to JSON for Rails
|
|
const jsonData = {};
|
|
for (let [key, value] of formData.entries()) {
|
|
// Skip Rails form helper fields
|
|
if (key === 'utf8' || key === '_method' || key === 'authenticity_token') {
|
|
continue;
|
|
}
|
|
|
|
// Handle nested attributes properly - support multiple levels
|
|
if (key.includes('[') && key.includes(']')) {
|
|
// Parse nested field names like attribute_field[field_options][input_size]
|
|
const keyParts = key.split(/[\[\]]+/).filter(part => part !== '');
|
|
|
|
let current = jsonData;
|
|
for (let i = 0; i < keyParts.length - 1; i++) {
|
|
const part = keyParts[i];
|
|
if (!current[part]) {
|
|
current[part] = {};
|
|
}
|
|
current = current[part];
|
|
}
|
|
|
|
const finalKey = keyParts[keyParts.length - 1];
|
|
current[finalKey] = value;
|
|
} else {
|
|
jsonData[key] = value;
|
|
}
|
|
}
|
|
|
|
console.log('Converted form data:', jsonData);
|
|
|
|
// Get the actual HTTP method from Rails form
|
|
let httpMethod = form.method.toUpperCase();
|
|
|
|
// Check for Rails method override (for PUT/PATCH/DELETE)
|
|
const methodInput = form.querySelector('input[name="_method"]');
|
|
if (methodInput) {
|
|
httpMethod = methodInput.value.toUpperCase();
|
|
}
|
|
|
|
console.log('Submitting form with method:', httpMethod, 'to:', form.action);
|
|
|
|
// Submit form via fetch
|
|
fetch(form.action, {
|
|
method: httpMethod,
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify(jsonData)
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Form submitted successfully:', data);
|
|
|
|
// Re-enable submit button
|
|
if (submitButton) {
|
|
submitButton.disabled = false;
|
|
if (submitButton.tagName === 'INPUT') {
|
|
submitButton.value = originalText;
|
|
} else {
|
|
submitButton.textContent = originalText;
|
|
}
|
|
}
|
|
|
|
// Show success notification
|
|
if (data.message) {
|
|
showNotification(data.message, 'success');
|
|
}
|
|
|
|
// Handle category form updates
|
|
if (form.action.includes('/attribute_categories/')) {
|
|
handleCategoryFormSuccess(form, data);
|
|
}
|
|
|
|
// Handle field form updates
|
|
if (form.action.includes('/attribute_fields/')) {
|
|
handleFieldFormSuccess(form, data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Form submission failed:', error);
|
|
|
|
// Re-enable submit button
|
|
if (submitButton) {
|
|
submitButton.disabled = false;
|
|
if (submitButton.tagName === 'INPUT') {
|
|
submitButton.value = originalText;
|
|
} else {
|
|
submitButton.textContent = originalText;
|
|
}
|
|
}
|
|
|
|
showNotification('Failed to save changes', 'error');
|
|
});
|
|
}
|
|
|
|
// Setup remote form handlers for dynamically loaded forms
|
|
function setupRemoteFormHandlers() {
|
|
// Handle successful form submissions (backup for Rails UJS if it works)
|
|
document.addEventListener('ajax:success', function(event) {
|
|
const form = event.target;
|
|
if (!form.matches('form[data-remote="true"]')) return;
|
|
|
|
const response = event.detail[0];
|
|
console.log('Form submitted successfully via Rails UJS:', response);
|
|
|
|
// Show success notification
|
|
if (response.message) {
|
|
showNotification(response.message, 'success');
|
|
}
|
|
|
|
// Handle category form updates
|
|
if (form.action.includes('/attribute_categories/')) {
|
|
handleCategoryFormSuccess(form, response);
|
|
}
|
|
|
|
// Handle field form updates
|
|
if (form.action.includes('/attribute_fields/')) {
|
|
handleFieldFormSuccess(form, response);
|
|
}
|
|
});
|
|
|
|
// Handle form submission errors (backup for Rails UJS if it works)
|
|
document.addEventListener('ajax:error', function(event) {
|
|
const form = event.target;
|
|
if (!form.matches('form[data-remote="true"]')) return;
|
|
|
|
const response = event.detail[0];
|
|
console.error('Form submission failed via Rails UJS:', response);
|
|
|
|
let errorMessage = 'Failed to save changes';
|
|
if (response && response.error) {
|
|
errorMessage = response.error;
|
|
}
|
|
|
|
showNotification(errorMessage, 'error');
|
|
});
|
|
}
|
|
|
|
// Handle successful category form submission
|
|
function handleCategoryFormSuccess(form, response) {
|
|
// Extract category ID from form action URL
|
|
const matches = form.action.match(/\/attribute_categories\/(\d+)/);
|
|
if (!matches) return;
|
|
|
|
const categoryId = matches[1];
|
|
const categoryCard = document.querySelector(`[data-category-id="${categoryId}"]`);
|
|
if (!categoryCard) return;
|
|
|
|
// Update category display if label changed
|
|
if (response.category && response.category.label) {
|
|
const labelElement = categoryCard.querySelector('.category-label');
|
|
if (labelElement) {
|
|
labelElement.textContent = response.category.label;
|
|
}
|
|
}
|
|
|
|
// Update category icon if changed
|
|
if (response.category && response.category.icon) {
|
|
const iconElement = categoryCard.querySelector('.category-icon i');
|
|
if (iconElement) {
|
|
iconElement.textContent = response.category.icon;
|
|
}
|
|
}
|
|
|
|
// Handle visibility changes
|
|
if (response.category && typeof response.category.hidden !== 'undefined') {
|
|
const isHidden = response.category.hidden;
|
|
updateCategoryVisibilityUI(categoryId, isHidden);
|
|
}
|
|
}
|
|
|
|
// Handle successful field form submission
|
|
function handleFieldFormSuccess(form, response) {
|
|
// Extract field ID from form action URL
|
|
const matches = form.action.match(/\/attribute_fields\/(\d+)/);
|
|
if (!matches) return;
|
|
|
|
const fieldId = matches[1];
|
|
const fieldItem = document.querySelector(`[data-field-id="${fieldId}"]`);
|
|
if (!fieldItem) return;
|
|
|
|
// Update field display if label changed
|
|
if (response.field && response.field.label) {
|
|
const labelElement = fieldItem.querySelector('.field-label');
|
|
if (labelElement) {
|
|
labelElement.textContent = response.field.label;
|
|
}
|
|
}
|
|
|
|
// Handle visibility changes
|
|
if (response.field && typeof response.field.hidden !== 'undefined') {
|
|
const isHidden = response.field.hidden;
|
|
updateFieldVisibilityUI(fieldId, isHidden);
|
|
}
|
|
}
|
|
|
|
// Update category visibility UI elements
|
|
function updateCategoryVisibilityUI(categoryId, isHidden) {
|
|
const categoryCard = document.querySelector(`[data-category-id="${categoryId}"]`);
|
|
if (!categoryCard) return;
|
|
|
|
// Update visibility toggle button
|
|
const button = categoryCard.querySelector('.category-visibility-toggle');
|
|
if (button) {
|
|
button.setAttribute('data-hidden', isHidden);
|
|
button.setAttribute('title', isHidden ? 'Hidden category - Click to show' : 'Visible category - Click to hide');
|
|
|
|
const eyeIcon = button.querySelector('svg');
|
|
if (eyeIcon) {
|
|
if (isHidden) {
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>';
|
|
} else {
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update category card styling
|
|
if (isHidden) {
|
|
categoryCard.classList.add('border-gray-300');
|
|
categoryCard.style.borderColor = '';
|
|
categoryCard.querySelector('.category-header').style.backgroundColor = '#f9fafb';
|
|
categoryCard.querySelector('.category-icon i').classList.add('text-gray-400');
|
|
categoryCard.querySelector('.category-icon i').style.color = '';
|
|
} else {
|
|
categoryCard.classList.remove('border-gray-300');
|
|
const contentTypeColor = getComputedStyle(document.documentElement).getPropertyValue('--content-type-color') || '#6366f1';
|
|
categoryCard.style.borderColor = contentTypeColor;
|
|
categoryCard.querySelector('.category-header').style.backgroundColor = contentTypeColor + '20';
|
|
categoryCard.querySelector('.category-icon i').classList.remove('text-gray-400');
|
|
categoryCard.querySelector('.category-icon i').style.color = contentTypeColor;
|
|
}
|
|
|
|
// Update hidden status text
|
|
const statusText = categoryCard.querySelector('.category-label').parentElement.querySelector('p');
|
|
if (statusText) {
|
|
if (isHidden) {
|
|
if (!statusText.textContent.includes('— Hidden')) {
|
|
statusText.innerHTML += '<span class="text-gray-400 ml-2">— Hidden</span>';
|
|
}
|
|
} else {
|
|
statusText.innerHTML = statusText.innerHTML.replace('<span class="text-gray-400 ml-2">— Hidden</span>', '');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update field visibility UI elements
|
|
function updateFieldVisibilityUI(fieldId, isHidden) {
|
|
const fieldItem = document.querySelector(`[data-field-id="${fieldId}"]`);
|
|
if (!fieldItem) return;
|
|
|
|
// Update visibility toggle button
|
|
const button = fieldItem.querySelector('.field-visibility-toggle');
|
|
if (button) {
|
|
button.setAttribute('data-hidden', isHidden);
|
|
button.setAttribute('title', isHidden ? 'Hidden field - Click to show' : 'Visible field - Click to hide');
|
|
|
|
const eyeIcon = button.querySelector('svg');
|
|
if (eyeIcon) {
|
|
if (isHidden) {
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>';
|
|
} else {
|
|
eyeIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update field item styling
|
|
if (isHidden) {
|
|
fieldItem.classList.add('bg-gray-50', 'border-gray-200');
|
|
fieldItem.classList.remove('bg-white');
|
|
fieldItem.querySelector('.field-label').classList.add('text-gray-500');
|
|
fieldItem.querySelector('.field-label').classList.remove('text-gray-800');
|
|
} else {
|
|
fieldItem.classList.remove('bg-gray-50', 'border-gray-200');
|
|
fieldItem.classList.add('bg-white');
|
|
fieldItem.querySelector('.field-label').classList.remove('text-gray-500');
|
|
fieldItem.querySelector('.field-label').classList.add('text-gray-800');
|
|
}
|
|
|
|
// Update hidden status text
|
|
const fieldInfo = fieldItem.querySelector('.text-xs.text-gray-500');
|
|
if (fieldInfo) {
|
|
if (isHidden) {
|
|
if (!fieldInfo.textContent.includes('— Hidden')) {
|
|
fieldInfo.innerHTML += '<span class="ml-1.5 text-gray-400">— Hidden</span>';
|
|
}
|
|
} else {
|
|
fieldInfo.innerHTML = fieldInfo.innerHTML.replace('<span class="ml-1.5 text-gray-400">— Hidden</span>', '');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update category configuration form position field after drag and drop
|
|
function updateCategoryConfigPosition(categoryId, newPosition) {
|
|
// Check if the category config form is currently open
|
|
const alpineElement = document.querySelector('.attributes-editor');
|
|
if (alpineElement && alpineElement._x_dataStack) {
|
|
const alpine = alpineElement._x_dataStack[0];
|
|
|
|
// Only update if this category's config form is currently open
|
|
if (alpine.selectedCategory == categoryId) {
|
|
const categoryConfigContainer = document.getElementById('category-config-container');
|
|
if (categoryConfigContainer) {
|
|
const positionInput = categoryConfigContainer.querySelector('input[name="attribute_category[position]"]');
|
|
if (positionInput) {
|
|
console.log(`Updating category ${categoryId} position input from ${positionInput.value} to ${newPosition}`);
|
|
positionInput.value = newPosition;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update field configuration form position field after drag and drop
|
|
function updateFieldConfigPosition(fieldId, newPosition) {
|
|
// Check if the field config form is currently open
|
|
const alpineElement = document.querySelector('.attributes-editor');
|
|
if (alpineElement && alpineElement._x_dataStack) {
|
|
const alpine = alpineElement._x_dataStack[0];
|
|
|
|
// Only update if this field's config form is currently open
|
|
if (alpine.selectedField == fieldId) {
|
|
const fieldConfigContainer = document.getElementById('field-config-container');
|
|
if (fieldConfigContainer) {
|
|
const positionInput = fieldConfigContainer.querySelector('input[name="attribute_field[position]"]');
|
|
if (positionInput) {
|
|
console.log(`Updating field ${fieldId} position input from ${positionInput.value} to ${newPosition}`);
|
|
positionInput.value = newPosition;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Show loading animation for configuration panels
|
|
function showConfigLoadingAnimation(containerId) {
|
|
const container = document.getElementById(containerId);
|
|
if (!container) return;
|
|
|
|
// Create loading bar element
|
|
const loadingBar = document.createElement('div');
|
|
loadingBar.className = 'config-loading-bar';
|
|
loadingBar.innerHTML = `
|
|
<div class="loading-bar-container">
|
|
<div class="loading-bar-progress"></div>
|
|
</div>
|
|
<div class="loading-content">
|
|
<div class="animate-pulse">
|
|
<div class="h-4 bg-gray-200 rounded w-1/4 mb-4"></div>
|
|
<div class="space-y-3">
|
|
<div class="h-3 bg-gray-200 rounded w-3/4"></div>
|
|
<div class="h-3 bg-gray-200 rounded w-1/2"></div>
|
|
<div class="h-3 bg-gray-200 rounded w-5/6"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Add loading styles
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
.config-loading-bar {
|
|
position: relative;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.loading-bar-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 3px;
|
|
background-color: #f3f4f6;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.loading-bar-progress {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--content-type-color, #6366f1) 0%, rgba(99, 102, 241, 0.6) 50%, var(--content-type-color, #6366f1) 100%);
|
|
background-size: 200% 100%;
|
|
animation: loading-slide 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes loading-slide {
|
|
0% {
|
|
transform: translateX(-100%);
|
|
}
|
|
100% {
|
|
transform: translateX(100%);
|
|
}
|
|
}
|
|
|
|
.loading-content {
|
|
margin-top: 1rem;
|
|
}
|
|
`;
|
|
|
|
// Add styles to head if not already present
|
|
if (!document.querySelector('.config-loading-styles')) {
|
|
style.classList.add('config-loading-styles');
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
// Replace container content with loading animation
|
|
container.innerHTML = '';
|
|
container.appendChild(loadingBar);
|
|
}
|
|
|
|
// Hide loading animation for configuration panels
|
|
function hideConfigLoadingAnimation(containerId) {
|
|
// The loading animation will be replaced when the actual content loads
|
|
// This function is called after the content is set, so it's mainly for cleanup
|
|
// and error handling scenarios
|
|
|
|
// Remove any loading-specific styles or elements if needed
|
|
const container = document.getElementById(containerId);
|
|
if (container) {
|
|
const loadingBar = container.querySelector('.config-loading-bar');
|
|
if (loadingBar) {
|
|
loadingBar.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Show loading animation for suggestions containers
|
|
function showSuggestionsLoadingAnimation(container) {
|
|
if (!container) return;
|
|
|
|
// Create compact loading indicator for suggestions
|
|
const loadingIndicator = document.createElement('div');
|
|
loadingIndicator.className = 'suggestions-loading-indicator';
|
|
loadingIndicator.innerHTML = `
|
|
<div class="loading-bar-container">
|
|
<div class="loading-bar-progress"></div>
|
|
</div>
|
|
<div class="loading-content">
|
|
<div class="loading-dots">
|
|
<div class="dot"></div>
|
|
<div class="dot"></div>
|
|
<div class="dot"></div>
|
|
</div>
|
|
<span class="loading-text">Loading suggestions...</span>
|
|
</div>
|
|
`;
|
|
|
|
// Add suggestion loading styles if not already present
|
|
if (!document.querySelector('.suggestions-loading-styles')) {
|
|
const style = document.createElement('style');
|
|
style.classList.add('suggestions-loading-styles');
|
|
style.textContent = `
|
|
.suggestions-loading-indicator {
|
|
position: relative;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 0.375rem;
|
|
background-color: #f9fafb;
|
|
margin: 0.5rem 0;
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-bar-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
background-color: #f3f4f6;
|
|
overflow: hidden;
|
|
border-radius: 0.375rem 0.375rem 0 0;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-bar-progress {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--content-type-color, #6366f1) 0%, rgba(99, 102, 241, 0.6) 50%, var(--content-type-color, #6366f1) 100%);
|
|
background-size: 200% 100%;
|
|
animation: loading-slide 1.2s ease-in-out infinite;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-top: 0.25rem;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-dots {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-dots .dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background-color: #9ca3af;
|
|
margin: 0 2px;
|
|
animation: loading-dots 1.4s ease-in-out infinite both;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-dots .dot:nth-child(1) {
|
|
animation-delay: -0.32s;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-dots .dot:nth-child(2) {
|
|
animation-delay: -0.16s;
|
|
}
|
|
|
|
.suggestions-loading-indicator .loading-text {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
font-weight: 500;
|
|
}
|
|
|
|
@keyframes loading-dots {
|
|
0%, 80%, 100% {
|
|
transform: scale(0.8);
|
|
opacity: 0.5;
|
|
}
|
|
40% {
|
|
transform: scale(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
// Replace container content with loading animation
|
|
container.innerHTML = '';
|
|
container.appendChild(loadingIndicator);
|
|
}
|
|
|
|
// Detect screen size changes to adjust UI
|
|
window.addEventListener('resize', function() {
|
|
const alpine = Alpine.getRoot(document.querySelector('.attributes-editor'));
|
|
if (alpine && alpine.$data) {
|
|
if (window.innerWidth >= 768) {
|
|
alpine.$data.activePanel = 'both';
|
|
} else if (alpine.$data.configuring) {
|
|
alpine.$data.activePanel = 'config';
|
|
} else {
|
|
alpine.$data.activePanel = 'template';
|
|
}
|
|
}
|
|
}); |