// Categories page JavaScript
let routes = {};
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
if (window.categoryRoutes) {
routes = window.categoryRoutes;
loadCategoryTrees();
}
});
function loadCategoryTrees() {
loadM1Tree();
loadM2Tree();
}
function loadM1Tree() {
const container = document.getElementById('m1-tree-container');
if (!container) return;
container.innerHTML = '
Loading categories...
';
fetch(routes.magento1CategoryTree)
.then(response => response.json())
.then(data => {
if (data.success) {
container.innerHTML = '';
if (data.tree && data.tree.length > 0) {
renderTree(container, data.tree, 'm1');
} else {
container.innerHTML = 'No categories found
';
}
} else {
container.innerHTML = `Error: ${data.message || 'Failed to load categories'}
`;
}
})
.catch(error => {
container.innerHTML = `Error: ${error.message}
`;
});
}
function loadM2Tree() {
const container = document.getElementById('m2-tree-container');
if (!container) return;
container.innerHTML = 'Loading categories...
';
fetch(routes.magento2CategoryTree)
.then(response => response.json())
.then(data => {
if (data.success) {
container.innerHTML = '';
if (data.tree && data.tree.length > 0) {
renderTree(container, data.tree, 'm2');
} else {
container.innerHTML = 'No categories found
';
}
} else {
container.innerHTML = `Error: ${data.message || 'Failed to load categories'}
`;
}
})
.catch(error => {
container.innerHTML = `Error: ${error.message}
`;
});
}
function renderTree(container, nodes, source = 'm2') {
nodes.forEach(node => {
const nodeElement = createTreeNode(node, source);
container.appendChild(nodeElement);
});
}
function createTreeNode(node, source = 'm2') {
const nodeDiv = document.createElement('div');
nodeDiv.className = 'tree-node';
const hasChildren = node.children && node.children.length > 0;
const canDelete = node.id != 0 && node.id != 1;
const itemDiv = document.createElement('div');
itemDiv.className = 'tree-node-item';
const toggle = document.createElement('span');
toggle.className = hasChildren ? 'tree-toggle collapsed' : 'tree-toggle leaf';
if (hasChildren) {
toggle.onclick = function(e) {
e.stopPropagation();
toggleNode(this);
};
}
const label = document.createElement('div');
label.className = 'tree-label';
const labelText = document.createElement('span');
labelText.className = 'tree-label-text';
labelText.textContent = `[${node.id}] ${node.name || 'Unnamed Category'}`;
const badge = document.createElement('span');
badge.className = `tree-badge ${node.is_active ? 'active' : 'inactive'}`;
badge.textContent = node.is_active ? 'Active' : 'Inactive';
label.appendChild(labelText);
label.appendChild(badge);
// Add rename button for Magento 2 categories only (except system root)
if (canDelete && source === 'm2') {
const renameBtn = document.createElement('button');
renameBtn.className = 'tree-rename-btn';
renameBtn.textContent = 'Rename';
renameBtn.title = 'Rename this category';
renameBtn.onclick = function(e) {
e.stopPropagation();
startRenameCategory(nodeDiv, node, source);
};
label.appendChild(renameBtn);
}
if (canDelete) {
const deleteBtn = document.createElement('button');
deleteBtn.className = 'tree-delete-btn';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = function(e) {
e.stopPropagation();
showDeleteConfirmPopup(deleteBtn, node.id, node.name, source, hasChildren, node.children ? node.children.length : 0);
};
label.appendChild(deleteBtn);
}
itemDiv.appendChild(toggle);
itemDiv.appendChild(label);
nodeDiv.appendChild(itemDiv);
if (hasChildren) {
const childrenDiv = document.createElement('div');
childrenDiv.className = 'tree-children';
node.children.forEach(child => {
childrenDiv.appendChild(createTreeNode(child, source));
});
nodeDiv.appendChild(childrenDiv);
}
return nodeDiv;
}
function toggleNode(toggleElement) {
const nodeItem = toggleElement.parentElement;
const nodeDiv = nodeItem.parentElement;
const childrenDivs = nodeDiv.querySelectorAll('.tree-children');
if (childrenDivs.length > 0) {
let isExpanded = false;
childrenDivs.forEach(div => {
if (div.style.display !== 'none') {
isExpanded = true;
}
});
if (isExpanded) {
childrenDivs.forEach(div => {
div.style.display = 'none';
});
toggleElement.classList.remove('expanded');
toggleElement.classList.add('collapsed');
} else {
childrenDivs.forEach(div => {
div.style.display = 'block';
});
toggleElement.classList.remove('collapsed');
toggleElement.classList.add('expanded');
}
}
}
function showDeleteConfirmPopup(buttonElement, categoryId, categoryName, source, hasChildren, childrenCount) {
const existingPopup = document.querySelector('.delete-confirm-popup');
const existingOverlay = document.querySelector('.popup-overlay');
if (existingPopup) existingPopup.remove();
if (existingOverlay) existingOverlay.remove();
const overlay = document.createElement('div');
overlay.className = 'popup-overlay';
overlay.onclick = function() {
closeDeleteConfirmPopup();
};
document.body.appendChild(overlay);
const popup = document.createElement('div');
popup.className = 'delete-confirm-popup';
popup.style.position = 'fixed';
popup.style.top = '50%';
popup.style.left = '50%';
popup.style.transform = 'translate(-50%, -50%)';
popup.style.zIndex = '10000';
const title = document.createElement('h3');
title.textContent = 'Delete Category';
const message = document.createElement('p');
if (hasChildren) {
message.innerHTML = `Are you sure you want to delete "${categoryName}" and all ${childrenCount} subcategory(ies)? This action cannot be undone.`;
} else {
message.innerHTML = `Are you sure you want to delete the category "${categoryName}"? This action cannot be undone.`;
}
const buttonsDiv = document.createElement('div');
buttonsDiv.className = 'popup-buttons';
const cancelBtn = document.createElement('button');
cancelBtn.className = 'popup-btn popup-btn-cancel';
cancelBtn.textContent = 'Cancel';
cancelBtn.onclick = function(e) {
e.stopPropagation();
closeDeleteConfirmPopup();
};
const deleteBtn = document.createElement('button');
deleteBtn.className = 'popup-btn popup-btn-delete';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = function(e) {
e.stopPropagation();
closeDeleteConfirmPopup();
deleteCategory(categoryId, categoryName, source);
};
buttonsDiv.appendChild(cancelBtn);
buttonsDiv.appendChild(deleteBtn);
popup.appendChild(title);
popup.appendChild(message);
popup.appendChild(buttonsDiv);
document.body.appendChild(popup);
}
function closeDeleteConfirmPopup() {
const popup = document.querySelector('.delete-confirm-popup');
const overlay = document.querySelector('.popup-overlay');
if (popup) popup.remove();
if (overlay) overlay.remove();
}
function deleteCategory(categoryId, categoryName, source) {
const container = source === 'm1' ? document.getElementById('m1-tree-container') : document.getElementById('m2-tree-container');
if (!container) return;
const originalContent = container.innerHTML;
container.innerHTML = 'Deleting category...
';
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
const deleteRoute = routes.deleteCategory.replace(':id', categoryId);
fetch(deleteRoute, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({ source: source })
})
.then(response => response.json())
.then(data => {
if (data.success) {
if (source === 'm1') {
loadM1Tree();
} else {
loadM2Tree();
// Reload page to refresh statistics and M2 Categories Not Found in M1 section
location.reload();
}
} else {
container.innerHTML = originalContent;
alert('Error: ' + (data.message || 'Failed to delete category'));
}
})
.catch(error => {
container.innerHTML = originalContent;
alert('Error: ' + error.message);
});
}
// Start renaming a category
function startRenameCategory(nodeDiv, node, source) {
const label = nodeDiv.querySelector('.tree-label');
const labelText = label.querySelector('.tree-label-text');
const originalName = node.name || 'Unnamed Category';
// Hide the label text and badge
labelText.style.display = 'none';
const badge = label.querySelector('.tree-badge');
if (badge) badge.style.display = 'none';
// Hide rename and delete buttons
const renameBtn = label.querySelector('.tree-rename-btn');
const deleteBtn = label.querySelector('.tree-delete-btn');
if (renameBtn) renameBtn.style.display = 'none';
if (deleteBtn) deleteBtn.style.display = 'none';
// Create input field
const input = document.createElement('input');
input.type = 'text';
input.className = 'tree-rename-input';
input.value = originalName;
input.onclick = function(e) {
e.stopPropagation();
};
// Create action buttons
const actionsDiv = document.createElement('div');
actionsDiv.className = 'tree-rename-actions';
const saveBtn = document.createElement('button');
saveBtn.className = 'tree-rename-save-btn';
saveBtn.textContent = 'Save';
saveBtn.onclick = function(e) {
e.stopPropagation();
saveRenameCategory(nodeDiv, node, input.value, source);
};
const cancelBtn = document.createElement('button');
cancelBtn.className = 'tree-rename-cancel-btn';
cancelBtn.textContent = 'Cancel';
cancelBtn.onclick = function(e) {
e.stopPropagation();
cancelRenameCategory(nodeDiv, labelText, badge, renameBtn, deleteBtn);
};
actionsDiv.appendChild(saveBtn);
actionsDiv.appendChild(cancelBtn);
label.appendChild(input);
label.appendChild(actionsDiv);
// Focus and select input
input.focus();
input.select();
// Handle Enter and Escape keys
input.onkeydown = function(e) {
if (e.key === 'Enter') {
e.preventDefault();
saveRenameCategory(nodeDiv, node, input.value, source);
} else if (e.key === 'Escape') {
e.preventDefault();
cancelRenameCategory(nodeDiv, labelText, badge, renameBtn, deleteBtn);
}
};
}
// Save category rename
function saveRenameCategory(nodeDiv, node, newName, source) {
const categoryId = node.id;
const label = nodeDiv.querySelector('.tree-label');
const input = label.querySelector('.tree-rename-input');
const actionsDiv = label.querySelector('.tree-rename-actions');
if (!newName || newName.trim() === '') {
alert('Category name cannot be empty');
return;
}
// Disable input and buttons
input.disabled = true;
const saveBtn = actionsDiv.querySelector('.tree-rename-save-btn');
const cancelBtn = actionsDiv.querySelector('.tree-rename-cancel-btn');
if (saveBtn) saveBtn.disabled = true;
if (cancelBtn) cancelBtn.disabled = true;
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
const renameRoute = routes.renameCategory.replace(':id', categoryId);
fetch(renameRoute, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({ name: newName.trim() })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update the node name
node.name = data.new_name;
// Update the label text
const labelText = label.querySelector('.tree-label-text');
labelText.textContent = `[${node.id}] ${data.new_name}`;
// Restore display
labelText.style.display = '';
const badge = label.querySelector('.tree-badge');
if (badge) badge.style.display = '';
const renameBtn = label.querySelector('.tree-rename-btn');
const deleteBtn = label.querySelector('.tree-delete-btn');
if (renameBtn) renameBtn.style.display = '';
if (deleteBtn) deleteBtn.style.display = '';
// Remove input and actions
label.removeChild(input);
label.removeChild(actionsDiv);
// Reload M2 Categories Not Found in M1 section
loadM2CategoriesNotInM1();
} else {
alert('Error: ' + (data.message || 'Failed to rename category'));
// Re-enable input and buttons
input.disabled = false;
if (saveBtn) saveBtn.disabled = false;
if (cancelBtn) cancelBtn.disabled = false;
}
})
.catch(error => {
alert('Error: ' + error.message);
// Re-enable input and buttons
input.disabled = false;
if (saveBtn) saveBtn.disabled = false;
if (cancelBtn) cancelBtn.disabled = false;
});
}
// Cancel category rename
function cancelRenameCategory(nodeDiv, labelText, badge, renameBtn, deleteBtn) {
const label = nodeDiv.querySelector('.tree-label');
const input = label.querySelector('.tree-rename-input');
const actionsDiv = label.querySelector('.tree-rename-actions');
// Restore display
labelText.style.display = '';
if (badge) badge.style.display = '';
if (renameBtn) renameBtn.style.display = '';
if (deleteBtn) deleteBtn.style.display = '';
// Remove input and actions
if (input) label.removeChild(input);
if (actionsDiv) label.removeChild(actionsDiv);
}
// Load M2 Categories Not Found in M1 section
function loadM2CategoriesNotInM1() {
const tbody = document.getElementById('m2-categories-not-in-m1-tbody');
const wrapper = document.getElementById('m2-categories-not-in-m1-table-wrapper');
const totalDiv = document.getElementById('m2-categories-not-in-m1-total');
const emptyDiv = document.getElementById('m2-categories-not-in-m1-empty');
if (!tbody && !wrapper && !totalDiv && !emptyDiv) {
// Section doesn't exist on this page
return;
}
fetch(routes.m2CategoriesNotInM1)
.then(response => response.json())
.then(data => {
if (data.success) {
const categories = data.categories || [];
const count = data.count || 0;
// Remove existing content
if (tbody) tbody.innerHTML = '';
if (totalDiv) totalDiv.style.display = 'none';
if (emptyDiv) emptyDiv.style.display = 'none';
if (wrapper) wrapper.style.display = 'none';
if (count > 0) {
// Show table and populate it
if (wrapper) wrapper.style.display = 'block';
if (totalDiv) {
totalDiv.style.display = 'block';
totalDiv.innerHTML = `Total: ${count} ${count === 1 ? 'category' : 'categories'} found in Magento 2 but not in Magento 1.`;
}
if (tbody) {
categories.forEach(category => {
const row = document.createElement('tr');
row.style.borderBottom = '1px solid #dee2e6';
const activeClass = category.is_active ? 'active' : 'inactive';
const activeText = category.is_active ? 'Active' : 'Inactive';
let rootCategoryHtml = 'N/A';
if (category.root_category_name && category.root_category_name !== 'N/A') {
rootCategoryHtml = `${escapeHtml(category.root_category_name)}`;
if (category.root_category_id) {
rootCategoryHtml += ` (ID: ${category.root_category_id})`;
}
}
// Create delete button with event listener
const deleteBtn = document.createElement('button');
deleteBtn.className = 'tree-delete-btn';
deleteBtn.textContent = 'Delete';
deleteBtn.title = 'Delete this category';
deleteBtn.onclick = function(e) {
e.stopPropagation();
showDeleteConfirmPopup(deleteBtn, category.entity_id, category.name, 'm2', false, 0);
};
row.innerHTML = `
${category.entity_id} |
${escapeHtml(category.name)} |
${category.level} |
${activeText}
|
${rootCategoryHtml} |
${escapeHtml(category.path)} |
|
`;
// Append delete button to the actions cell
const actionsCell = row.querySelector('td:last-child');
if (actionsCell) {
actionsCell.appendChild(deleteBtn);
}
tbody.appendChild(row);
});
}
} else {
// Show empty message
if (emptyDiv) {
emptyDiv.style.display = 'block';
}
}
}
})
.catch(error => {
console.error('Error loading M2 categories not in M1:', error);
});
}
// Helper function to escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}