// Products page JavaScript
let routes = {};
let csrfToken = '';
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
if (window.productRoutes) {
routes = window.productRoutes;
csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
}
});
function startProductMigration(dryRun) {
const button = dryRun ? document.getElementById('dryRunProductMigrationBtn') : document.getElementById('startProductMigrationBtn');
const otherButton = dryRun ? document.getElementById('startProductMigrationBtn') : document.getElementById('dryRunProductMigrationBtn');
const originalText = button.textContent;
button.disabled = true;
otherButton.disabled = true;
button.textContent = dryRun ? 'Running Dry Run...' : 'Migrating...';
button.style.cursor = 'not-allowed';
const logContent = document.getElementById('productMigrationLogContent');
logContent.innerHTML = '
' + (dryRun ? 'Running dry run...' : 'Starting migration...') + '
';
fetch(routes.migrateProducts, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({ dry_run: dryRun })
})
.then(response => response.json())
.then(data => {
button.disabled = false;
otherButton.disabled = false;
button.textContent = originalText;
button.style.cursor = 'pointer';
if (data.success) {
const successEntry = document.createElement('div');
successEntry.className = 'log-entry success';
successEntry.textContent = `✓ ${dryRun ? 'Dry run' : 'Migration'} completed! Added: ${data.added || 0}, Updated: ${data.updated || 0}, Errors: ${data.errors || 0}`;
logContent.appendChild(successEntry);
if (data.log && data.log.length > 0) {
data.log.forEach(log => {
const entry = document.createElement('div');
entry.className = 'log-entry ' + (log.includes('ERROR') ? 'error' : 'success');
entry.textContent = log;
logContent.appendChild(entry);
});
}
if (data.missing_attributes && data.missing_attributes.length > 0) {
const missingSection = document.getElementById('missingAttributesSection');
const tbody = document.getElementById('missingAttributesTableBody');
tbody.innerHTML = '';
data.missing_attributes.forEach(attr => {
const row = document.createElement('tr');
row.innerHTML = `
${attr.attribute_code} |
${attr.frontend_label || 'N/A'} |
${attr.backend_type || 'N/A'} |
`;
tbody.appendChild(row);
});
missingSection.style.display = 'block';
}
const logContainer = document.getElementById('productMigrationLogContainer');
logContainer.scrollTop = logContainer.scrollHeight;
} else {
const errorEntry = document.createElement('div');
errorEntry.className = 'log-entry error';
errorEntry.textContent = '✗ ' + (dryRun ? 'Dry run' : 'Migration') + ' failed: ' + (data.message || 'Unknown error');
logContent.appendChild(errorEntry);
}
})
.catch(error => {
button.disabled = false;
otherButton.disabled = false;
button.textContent = originalText;
button.style.cursor = 'pointer';
const errorEntry = document.createElement('div');
errorEntry.className = 'log-entry error';
errorEntry.textContent = '✗ Error: ' + error.message;
logContent.appendChild(errorEntry);
});
}
function deleteM2Product(productId, productName, productSku) {
if (!confirm(`Are you sure you want to delete product "${productName}" (SKU: ${productSku})?`)) {
return;
}
const url = routes.deleteProduct.replace(':id', productId);
fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Product deleted successfully!');
location.reload();
} else {
alert('Error: ' + (data.message || 'Failed to delete product'));
}
})
.catch(error => {
alert('Error: ' + error.message);
});
}
function deleteProductsAboveM1Max() {
if (!confirm('Are you sure you want to delete all Magento 2 products with entity_id greater than the maximum Magento 1 product ID? This action cannot be undone.')) {
return;
}
const button = document.getElementById('deleteProductsAboveM1MaxBtn');
const originalText = button.textContent;
button.disabled = true;
button.textContent = 'Deleting...';
fetch(routes.deleteProductsAboveM1Max, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => response.json())
.then(data => {
button.disabled = false;
button.textContent = originalText;
if (data.success) {
alert(`Successfully deleted ${data.deleted || 0} products.`);
location.reload();
} else {
alert('Error: ' + (data.message || 'Failed to delete products'));
}
})
.catch(error => {
button.disabled = false;
button.textContent = originalText;
alert('Error: ' + error.message);
});
}
function syncProductCategories() {
if (!confirm('Are you sure you want to sync product category assignments from Magento 1 to Magento 2?')) {
return;
}
const button = document.getElementById('syncProductCategoriesBtn');
const originalText = button.textContent;
button.disabled = true;
button.textContent = 'Syncing...';
fetch(routes.syncProductCategories, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => response.json())
.then(data => {
button.disabled = false;
button.textContent = originalText;
if (data.success) {
alert(`Sync completed! Updated: ${data.updated || 0}, Skipped: ${data.skipped || 0}, Errors: ${data.errors || 0}`);
} else {
alert('Error: ' + (data.message || 'Sync failed'));
}
})
.catch(error => {
button.disabled = false;
button.textContent = originalText;
alert('Error: ' + error.message);
});
}
function loadM1CategoryTreeWithProducts() {
const container = document.getElementById('m1-category-products-tree-container');
if (!container) return;
container.innerHTML = 'Loading categories and products...
';
fetch(routes.magento1CategoryTreeWithProducts)
.then(response => response.json())
.then(data => {
if (data.success) {
container.innerHTML = '';
if (data.tree && data.tree.length > 0) {
renderCategoryTreeWithProducts(container, data.tree);
} else {
container.innerHTML = 'No categories found
';
}
} else {
container.innerHTML = `Error: ${data.message || 'Failed to load categories'}
`;
}
})
.catch(error => {
container.innerHTML = `Error: ${error.message}
`;
});
}
function loadM2CategoryTreeWithProducts() {
const container = document.getElementById('m2-category-products-tree-container');
if (!container) return;
container.innerHTML = 'Loading categories and products...
';
fetch(routes.magento2CategoryTreeWithProducts)
.then(response => response.json())
.then(data => {
if (data.success) {
container.innerHTML = '';
if (data.tree && data.tree.length > 0) {
renderCategoryTreeWithProducts(container, data.tree);
} else {
container.innerHTML = 'No categories found
';
}
} else {
container.innerHTML = `Error: ${data.message || 'Failed to load categories'}
`;
}
})
.catch(error => {
container.innerHTML = `Error: ${error.message}
`;
});
}
function renderCategoryTreeWithProducts(container, tree) {
tree.forEach(node => {
const nodeElement = createCategoryWithProductsNode(node);
container.appendChild(nodeElement);
});
}
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 createCategoryWithProductsNode(node) {
const nodeDiv = document.createElement('div');
nodeDiv.className = 'tree-node';
const hasChildren = node.children && node.children.length > 0;
const hasProducts = node.products && node.products.length > 0;
const productCount = node.product_count !== undefined ? node.product_count : (node.products ? node.products.length : 0);
const itemDiv = document.createElement('div');
itemDiv.className = 'tree-node-item';
const toggle = document.createElement('span');
toggle.className = (hasChildren || hasProducts) ? 'tree-toggle collapsed' : 'tree-toggle leaf';
if (hasChildren || hasProducts) {
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'} (${productCount} products)`;
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);
itemDiv.appendChild(toggle);
itemDiv.appendChild(label);
nodeDiv.appendChild(itemDiv);
// Add products section if products exist
if (hasProducts) {
const productsDiv = document.createElement('div');
productsDiv.className = 'tree-children';
productsDiv.style.display = 'none';
const productsHeader = document.createElement('div');
productsHeader.style.cssText = 'padding: 8px 12px; font-weight: 600; color: #667eea; background: #f0f0f0; border-radius: 4px; margin: 5px 0;';
productsHeader.textContent = `Products (${node.products.length}):`;
productsDiv.appendChild(productsHeader);
node.products.forEach(product => {
const productDiv = document.createElement('div');
productDiv.style.cssText = 'padding: 6px 12px 6px 30px; font-size: 0.9em; color: #666; border-left: 2px solid #e0e0e0; margin-left: 20px;';
productDiv.textContent = `ID: ${product.id || product.product_id || 'N/A'} | SKU: ${product.sku || 'N/A'} | Name: ${product.name || 'Unnamed Product'}`;
productsDiv.appendChild(productDiv);
});
nodeDiv.appendChild(productsDiv);
}
// Add children
if (hasChildren) {
const childrenDiv = document.createElement('div');
childrenDiv.className = 'tree-children';
childrenDiv.style.display = 'none';
node.children.forEach(child => {
childrenDiv.appendChild(createCategoryWithProductsNode(child));
});
nodeDiv.appendChild(childrenDiv);
}
return nodeDiv;
}
// Make functions available globally
window.startProductMigration = startProductMigration;
window.deleteM2Product = deleteM2Product;
window.deleteProductsAboveM1Max = deleteProductsAboveM1Max;
window.syncProductCategories = syncProductCategories;
window.loadM1CategoryTreeWithProducts = loadM1CategoryTreeWithProducts;
window.loadM2CategoryTreeWithProducts = loadM2CategoryTreeWithProducts;