// 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; }