709 lines
28 KiB
JavaScript
709 lines
28 KiB
JavaScript
// 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 = '<div class="tree-loading">Loading categories...</div>';
|
|
|
|
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 = '<div class="tree-loading">No categories found</div>';
|
|
}
|
|
} else {
|
|
container.innerHTML = `<div class="tree-error">Error: ${data.message || 'Failed to load categories'}</div>`;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
container.innerHTML = `<div class="tree-error">Error: ${error.message}</div>`;
|
|
});
|
|
}
|
|
|
|
function loadM2Tree() {
|
|
const container = document.getElementById('m2-tree-container');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '<div class="tree-loading">Loading categories...</div>';
|
|
|
|
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 = '<div class="tree-loading">No categories found</div>';
|
|
}
|
|
} else {
|
|
container.innerHTML = `<div class="tree-error">Error: ${data.message || 'Failed to load categories'}</div>`;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
container.innerHTML = `<div class="tree-error">Error: ${error.message}</div>`;
|
|
});
|
|
}
|
|
|
|
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 <strong style="color: #d32f2f; font-weight: bold;">"${categoryName}"</strong> and all ${childrenCount} subcategory(ies)? This action cannot be undone.`;
|
|
} else {
|
|
message.innerHTML = `Are you sure you want to delete the category <strong style="color: #d32f2f; font-weight: bold;">"${categoryName}"</strong>? 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 = '<div class="tree-loading">Deleting category...</div>';
|
|
|
|
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();
|
|
// Reload M1 Categories Not Found in M2 section
|
|
loadM1CategoriesNotInM2();
|
|
// Reload page to refresh statistics
|
|
location.reload();
|
|
} else {
|
|
loadM2Tree();
|
|
// Reload M2 Categories Not Found in M1 section
|
|
loadM2CategoriesNotInM1();
|
|
// Reload page to refresh statistics
|
|
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();
|
|
// Reload M1 Categories Not Found in M2 section (in case renamed M2 category now matches an M1 category)
|
|
loadM1CategoriesNotInM2();
|
|
} 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 = `<strong>Total:</strong> ${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 = '<span style="color: #999;">N/A</span>';
|
|
if (category.root_category_name && category.root_category_name !== 'N/A') {
|
|
rootCategoryHtml = `<span style="font-weight: 500;">${escapeHtml(category.root_category_name)}</span>`;
|
|
if (category.root_category_id) {
|
|
rootCategoryHtml += ` <span style="font-size: 0.85em; color: #666;">(ID: ${category.root_category_id})</span>`;
|
|
}
|
|
}
|
|
|
|
// 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 = `
|
|
<td style="padding: 10px 12px;">${category.entity_id}</td>
|
|
<td style="padding: 10px 12px; font-weight: 500;">${escapeHtml(category.name)}</td>
|
|
<td style="padding: 10px 12px;">${category.level}</td>
|
|
<td style="padding: 10px 12px;">
|
|
<span class="tree-badge ${activeClass}">${activeText}</span>
|
|
</td>
|
|
<td style="padding: 10px 12px;">${rootCategoryHtml}</td>
|
|
<td style="padding: 10px 12px; font-size: 0.9em; color: #666;">${escapeHtml(category.path)}</td>
|
|
<td style="padding: 10px 12px;"></td>
|
|
`;
|
|
|
|
// 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);
|
|
});
|
|
}
|
|
|
|
// Load M1 Categories Not Found in M2 section
|
|
function loadM1CategoriesNotInM2() {
|
|
const tbody = document.getElementById('m1-categories-not-in-m2-tbody');
|
|
const wrapper = document.getElementById('m1-categories-not-in-m2-table-wrapper');
|
|
const totalDiv = document.getElementById('m1-categories-not-in-m2-total');
|
|
const emptyDiv = document.getElementById('m1-categories-not-in-m2-empty');
|
|
|
|
if (!tbody && !wrapper && !totalDiv && !emptyDiv) {
|
|
// Section doesn't exist on this page
|
|
return;
|
|
}
|
|
|
|
fetch(routes.m1CategoriesNotInM2)
|
|
.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 = `<strong>Total:</strong> ${count} ${count === 1 ? 'category' : 'categories'} found in Magento 1 but not in Magento 2.`;
|
|
}
|
|
|
|
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 = '<span style="color: #999;">N/A</span>';
|
|
if (category.root_category_name && category.root_category_name !== 'N/A') {
|
|
rootCategoryHtml = `<span style="font-weight: 500;">${escapeHtml(category.root_category_name)}</span>`;
|
|
if (category.root_category_id) {
|
|
rootCategoryHtml += ` <span style="font-size: 0.85em; color: #666;">(ID: ${category.root_category_id})</span>`;
|
|
}
|
|
}
|
|
|
|
// Create migrate button with event listener
|
|
const migrateBtn = document.createElement('button');
|
|
migrateBtn.className = 'tree-migrate-btn';
|
|
migrateBtn.textContent = 'Migrate';
|
|
migrateBtn.title = 'Migrate this category to Magento 2';
|
|
migrateBtn.setAttribute('data-migrate-id', category.entity_id);
|
|
migrateBtn.onclick = function(e) {
|
|
e.stopPropagation();
|
|
migrateM1Category(category.entity_id, category.name, migrateBtn);
|
|
};
|
|
|
|
// 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, 'm1', false, 0);
|
|
};
|
|
|
|
row.innerHTML = `
|
|
<td style="padding: 10px 12px;">${category.entity_id}</td>
|
|
<td style="padding: 10px 12px; font-weight: 500;">${escapeHtml(category.name)}</td>
|
|
<td style="padding: 10px 12px;">${category.level}</td>
|
|
<td style="padding: 10px 12px;">
|
|
<span class="tree-badge ${activeClass}">${activeText}</span>
|
|
</td>
|
|
<td style="padding: 10px 12px;">${rootCategoryHtml}</td>
|
|
<td style="padding: 10px 12px; font-size: 0.9em; color: #666;">${escapeHtml(category.path)}</td>
|
|
<td style="padding: 10px 12px;"></td>
|
|
`;
|
|
|
|
// Append buttons to the actions cell
|
|
const actionsCell = row.querySelector('td:last-child');
|
|
if (actionsCell) {
|
|
actionsCell.appendChild(migrateBtn);
|
|
actionsCell.appendChild(deleteBtn);
|
|
}
|
|
|
|
tbody.appendChild(row);
|
|
});
|
|
}
|
|
} else {
|
|
// Show empty message
|
|
if (emptyDiv) {
|
|
emptyDiv.style.display = 'block';
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading M1 categories not in M2:', error);
|
|
});
|
|
}
|
|
|
|
// Migrate a single M1 category to M2
|
|
window.migrateM1Category = function(categoryId, categoryName, buttonElement) {
|
|
if (!confirm(`Are you sure you want to migrate the category "${categoryName}" (ID: ${categoryId}) to Magento 2?`)) {
|
|
return;
|
|
}
|
|
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
|
|
const migrateRoute = routes.migrateCategory.replace(':id', categoryId);
|
|
|
|
// Disable the migrate button
|
|
const button = buttonElement || document.querySelector(`button[data-migrate-id="${categoryId}"]`);
|
|
if (button) {
|
|
button.disabled = true;
|
|
button.textContent = 'Migrating...';
|
|
}
|
|
|
|
fetch(migrateRoute, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': csrfToken
|
|
},
|
|
body: JSON.stringify({ store_id: 0 })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert(`Category "${categoryName}" migrated successfully to Magento 2 (ID: ${data.m2_category_id})`);
|
|
|
|
// Reload both sections
|
|
loadM1CategoriesNotInM2();
|
|
loadM2CategoriesNotInM1();
|
|
|
|
// Reload M2 tree to show the new category
|
|
loadM2Tree();
|
|
} else {
|
|
alert('Error: ' + (data.message || 'Failed to migrate category'));
|
|
|
|
// Re-enable button
|
|
if (button) {
|
|
button.disabled = false;
|
|
button.textContent = 'Migrate';
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error: ' + error.message);
|
|
|
|
// Re-enable button
|
|
if (button) {
|
|
button.disabled = false;
|
|
button.textContent = 'Migrate';
|
|
}
|
|
});
|
|
};
|
|
|
|
// Helper function to escape HTML
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|