511 lines
20 KiB
JavaScript
511 lines
20 KiB
JavaScript
// Product URLs comparison functionality
|
|
|
|
let currentPage = 0;
|
|
let currentM1Page = 0;
|
|
let currentM2Page = 0;
|
|
const pageSize = 100;
|
|
let currentComparisonData = [];
|
|
let currentM1Data = [];
|
|
let currentM2Data = [];
|
|
let currentFilter = 'all';
|
|
|
|
// Compare URLs between M1 and M2
|
|
window.compareUrls = async function compareUrls() {
|
|
const sku = document.getElementById('skuFilter').value.trim();
|
|
const btn = document.getElementById('compareUrlsBtn');
|
|
|
|
btn.disabled = true;
|
|
btn.textContent = 'Comparing...';
|
|
|
|
try {
|
|
const response = await fetch(window.productUrlsRoutes.compareUrls + '?sku=' + encodeURIComponent(sku) + '&limit=' + pageSize + '&offset=' + (currentPage * pageSize), {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
currentComparisonData = data.comparison;
|
|
displayComparison(data);
|
|
updateSummary(data.summary);
|
|
document.getElementById('comparisonSection').style.display = 'block';
|
|
document.getElementById('summarySection').style.display = 'block';
|
|
} else {
|
|
alert('Error: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error comparing URLs:', error);
|
|
alert('Failed to compare URLs: ' + error.message);
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Compare URLs';
|
|
}
|
|
}
|
|
|
|
// Display comparison results
|
|
function displayComparison(data) {
|
|
const tbody = document.getElementById('comparisonTableBody');
|
|
tbody.innerHTML = '';
|
|
|
|
if (data.comparison.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; color: #999; padding: 20px;">No results found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
data.comparison.forEach(item => {
|
|
const row = document.createElement('tr');
|
|
const statusClass = getStatusClass(item.status);
|
|
const statusLabel = getStatusLabel(item.status);
|
|
|
|
row.innerHTML = `
|
|
<td>${escapeHtml(item.sku || 'N/A')}</td>
|
|
<td>${item.m1_product_id || '-'}</td>
|
|
<td>${item.m2_product_id || '-'}</td>
|
|
<td>${item.store_id || '-'}</td>
|
|
<td>${escapeHtml(item.m1_url || '-')}</td>
|
|
<td>${escapeHtml(item.m2_url || '-')}</td>
|
|
<td><span class="status-badge ${statusClass}">${statusLabel}</span></td>
|
|
`;
|
|
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
// Update pagination
|
|
updatePagination(data.total, currentPage);
|
|
document.getElementById('resultsCount').textContent = `Showing ${data.comparison.length} of ${data.total} results`;
|
|
}
|
|
|
|
// Update summary statistics
|
|
function updateSummary(summary) {
|
|
document.getElementById('summaryMatch').textContent = summary.match || 0;
|
|
document.getElementById('summaryMissingM2').textContent = summary.missing_in_m2 || 0;
|
|
document.getElementById('summaryMissingM1').textContent = summary.missing_in_m1 || 0;
|
|
document.getElementById('summaryDifferent').textContent = summary.different || 0;
|
|
}
|
|
|
|
// Get status class for styling
|
|
function getStatusClass(status) {
|
|
switch(status) {
|
|
case 'match': return 'status-match';
|
|
case 'missing_in_m2': return 'status-warning';
|
|
case 'missing_in_m1': return 'status-info';
|
|
case 'different': return 'status-error';
|
|
default: return '';
|
|
}
|
|
}
|
|
|
|
// Get status label
|
|
function getStatusLabel(status) {
|
|
switch(status) {
|
|
case 'match': return 'Match';
|
|
case 'missing_in_m2': return 'Missing in M2';
|
|
case 'missing_in_m1': return 'Missing in M1';
|
|
case 'different': return 'Different';
|
|
default: return status;
|
|
}
|
|
}
|
|
|
|
// Filter results by status
|
|
window.filterResults = function filterResults() {
|
|
const filter = document.getElementById('statusFilter').value;
|
|
currentFilter = filter;
|
|
|
|
// Re-fetch with filter (or filter client-side)
|
|
// For now, we'll filter client-side if we have all data
|
|
// In a real implementation, you might want to pass filter to the server
|
|
compareUrls();
|
|
}
|
|
|
|
// Load Magento 1 URLs
|
|
window.loadM1Urls = async function loadM1Urls() {
|
|
const sku = document.getElementById('skuFilter').value.trim();
|
|
const btn = document.getElementById('loadM1UrlsBtn');
|
|
|
|
btn.disabled = true;
|
|
btn.textContent = 'Loading...';
|
|
|
|
try {
|
|
const response = await fetch(window.productUrlsRoutes.getM1Urls + '?sku=' + encodeURIComponent(sku) + '&limit=' + pageSize + '&offset=' + (currentM1Page * pageSize), {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
currentM1Data = data;
|
|
displayM1Urls(data);
|
|
document.getElementById('m1UrlsSection').style.display = 'block';
|
|
} else {
|
|
alert('Error: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading M1 URLs:', error);
|
|
alert('Failed to load M1 URLs: ' + error.message);
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Load M1 URLs';
|
|
}
|
|
}
|
|
|
|
// Display M1 URLs
|
|
function displayM1Urls(data) {
|
|
const tbody = document.getElementById('m1UrlsTableBody');
|
|
tbody.innerHTML = '';
|
|
|
|
if (data.urls.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align: center; color: #999; padding: 20px;">No URLs found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
data.urls.forEach(item => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${item.entity_id || '-'}</td>
|
|
<td>${escapeHtml(item.sku || 'N/A')}</td>
|
|
<td>${item.store_id || '-'}</td>
|
|
<td>${escapeHtml(item.request_path || '-')}</td>
|
|
<td>${escapeHtml(item.target_path || '-')}</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
updateM1Pagination(data.total, currentM1Page);
|
|
}
|
|
|
|
// Load Magento 2 URLs
|
|
window.loadM2Urls = async function loadM2Urls() {
|
|
const sku = document.getElementById('skuFilter').value.trim();
|
|
const btn = document.getElementById('loadM2UrlsBtn');
|
|
|
|
btn.disabled = true;
|
|
btn.textContent = 'Loading...';
|
|
|
|
try {
|
|
const response = await fetch(window.productUrlsRoutes.getM2Urls + '?sku=' + encodeURIComponent(sku) + '&limit=' + pageSize + '&offset=' + (currentM2Page * pageSize), {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
currentM2Data = data;
|
|
displayM2Urls(data);
|
|
document.getElementById('m2UrlsSection').style.display = 'block';
|
|
} else {
|
|
alert('Error: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading M2 URLs:', error);
|
|
alert('Failed to load M2 URLs: ' + error.message);
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Load M2 URLs';
|
|
}
|
|
}
|
|
|
|
// Display M2 URLs
|
|
function displayM2Urls(data) {
|
|
const tbody = document.getElementById('m2UrlsTableBody');
|
|
tbody.innerHTML = '';
|
|
|
|
if (data.urls.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align: center; color: #999; padding: 20px;">No URLs found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
data.urls.forEach(item => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${item.entity_id || '-'}</td>
|
|
<td>${escapeHtml(item.sku || 'N/A')}</td>
|
|
<td>${item.store_id || '-'}</td>
|
|
<td>${escapeHtml(item.request_path || '-')}</td>
|
|
<td>${escapeHtml(item.target_path || '-')}</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
updateM2Pagination(data.total, currentM2Page);
|
|
}
|
|
|
|
// Pagination functions
|
|
window.changePage = function changePage(direction) {
|
|
currentPage += direction;
|
|
if (currentPage < 0) currentPage = 0;
|
|
compareUrls();
|
|
}
|
|
|
|
window.changeM1Page = function changeM1Page(direction) {
|
|
currentM1Page += direction;
|
|
if (currentM1Page < 0) currentM1Page = 0;
|
|
loadM1Urls();
|
|
}
|
|
|
|
window.changeM2Page = function changeM2Page(direction) {
|
|
currentM2Page += direction;
|
|
if (currentM2Page < 0) currentM2Page = 0;
|
|
loadM2Urls();
|
|
}
|
|
|
|
function updatePagination(total, currentPage) {
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
const pageInfo = document.getElementById('pageInfo');
|
|
const prevBtn = document.getElementById('prevPageBtn');
|
|
const nextBtn = document.getElementById('nextPageBtn');
|
|
|
|
pageInfo.textContent = `Page ${currentPage + 1} of ${totalPages}`;
|
|
prevBtn.disabled = currentPage === 0;
|
|
nextBtn.disabled = currentPage >= totalPages - 1;
|
|
}
|
|
|
|
function updateM1Pagination(total, currentPage) {
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
const pageInfo = document.getElementById('m1PageInfo');
|
|
const prevBtn = document.getElementById('m1PrevPageBtn');
|
|
const nextBtn = document.getElementById('m1NextPageBtn');
|
|
|
|
pageInfo.textContent = `Page ${currentPage + 1} of ${totalPages}`;
|
|
prevBtn.disabled = currentPage === 0;
|
|
nextBtn.disabled = currentPage >= totalPages - 1;
|
|
}
|
|
|
|
function updateM2Pagination(total, currentPage) {
|
|
const totalPages = Math.ceil(total / pageSize);
|
|
const pageInfo = document.getElementById('m2PageInfo');
|
|
const prevBtn = document.getElementById('m2PrevPageBtn');
|
|
const nextBtn = document.getElementById('m2NextPageBtn');
|
|
|
|
pageInfo.textContent = `Page ${currentPage + 1} of ${totalPages}`;
|
|
prevBtn.disabled = currentPage === 0;
|
|
nextBtn.disabled = currentPage >= totalPages - 1;
|
|
}
|
|
|
|
// Utility function to escape HTML
|
|
function escapeHtml(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// Compare single product SKU
|
|
window.compareSingleSku = async function compareSingleSku() {
|
|
const sku = document.getElementById('singleSkuInput').value.trim();
|
|
const btn = document.getElementById('compareSingleSkuBtn');
|
|
const section = document.getElementById('singleProductComparisonSection');
|
|
const content = document.getElementById('singleProductComparisonContent');
|
|
|
|
if (!sku) {
|
|
alert('Please enter a product SKU');
|
|
return;
|
|
}
|
|
|
|
btn.disabled = true;
|
|
btn.textContent = 'Comparing...';
|
|
content.innerHTML = '<div style="text-align: center; padding: 20px; color: #666;">Loading...</div>';
|
|
section.style.display = 'block';
|
|
document.getElementById('singleSkuDisplay').textContent = sku;
|
|
|
|
try {
|
|
const response = await fetch(window.productUrlsRoutes.compareSingleSku + '?sku=' + encodeURIComponent(sku), {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
displaySingleProductComparison(data);
|
|
} else {
|
|
content.innerHTML = '<div style="padding: 20px; color: #dc3545; background: #f8d7da; border-radius: 4px;">Error: ' + escapeHtml(data.message) + '</div>';
|
|
}
|
|
} catch (error) {
|
|
console.error('Error comparing single SKU:', error);
|
|
content.innerHTML = '<div style="padding: 20px; color: #dc3545; background: #f8d7da; border-radius: 4px;">Failed to compare URLs: ' + escapeHtml(error.message) + '</div>';
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Compare Product URLs';
|
|
}
|
|
}
|
|
|
|
// Display single product comparison results
|
|
function displaySingleProductComparison(data) {
|
|
const content = document.getElementById('singleProductComparisonContent');
|
|
|
|
if (!data.comparison || data.comparison.length === 0) {
|
|
content.innerHTML = '<div style="padding: 20px; color: #666; text-align: center;">No URLs found for this product SKU.</div>';
|
|
return;
|
|
}
|
|
|
|
let html = '<div style="margin-bottom: 20px;">';
|
|
html += '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;">';
|
|
html += '<div style="background: #FF8C42; padding: 15px; border-radius: 6px; color: white; text-align: center;">';
|
|
html += '<div style="font-size: 1.5em; font-weight: bold;">' + (data.summary.match || 0) + '</div>';
|
|
html += '<div>Matching Stores</div></div>';
|
|
html += '<div style="background: #FF6B35; padding: 15px; border-radius: 6px; color: white; text-align: center;">';
|
|
html += '<div style="font-size: 1.5em; font-weight: bold;">' + (data.summary.missing_in_m2 || 0) + '</div>';
|
|
html += '<div>Missing in M2</div></div>';
|
|
html += '<div style="background: #FFA366; padding: 15px; border-radius: 6px; color: white; text-align: center;">';
|
|
html += '<div style="font-size: 1.5em; font-weight: bold;">' + (data.summary.missing_in_m1 || 0) + '</div>';
|
|
html += '<div>Missing in M1</div></div>';
|
|
html += '<div style="background: #C43A0D; padding: 15px; border-radius: 6px; color: white; text-align: center;">';
|
|
html += '<div style="font-size: 1.5em; font-weight: bold;">' + (data.summary.different || 0) + '</div>';
|
|
html += '<div>Different URLs</div></div>';
|
|
html += '</div>';
|
|
|
|
html += '<div style="margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 6px;">';
|
|
html += '<div style="margin-bottom: 10px;"><strong>Product IDs:</strong> M1: ' + (data.m1_product_id || 'N/A') + ' | M2: ' + (data.m2_product_id || 'N/A') + '</div>';
|
|
html += '<div><strong>Total URLs:</strong> M1: ' + (data.summary.total_m1_urls || 0) + ' | M2: ' + (data.summary.total_m2_urls || 0) + ' | Stores: ' + (data.summary.total_stores || 0) + '</div>';
|
|
html += '</div>';
|
|
html += '</div>';
|
|
|
|
// Group by store
|
|
data.comparison.forEach(storeData => {
|
|
const statusClass = getStatusClass(storeData.status);
|
|
const statusLabel = getStatusLabel(storeData.status);
|
|
const storeId = storeData.store_id || 'Default';
|
|
|
|
html += '<div style="margin-top: 25px; border: 2px solid #ddd; border-radius: 8px; overflow: hidden;">';
|
|
html += '<div style="background: #f8f9fa; padding: 15px; border-bottom: 2px solid #ddd; display: flex; justify-content: space-between; align-items: center;">';
|
|
html += '<div><strong style="font-size: 1.1em;">Store ID: ' + storeId + '</strong></div>';
|
|
html += '<div><span class="status-badge ' + statusClass + '">' + statusLabel + '</span></div>';
|
|
html += '</div>';
|
|
|
|
html += '<div style="padding: 20px;">';
|
|
html += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">';
|
|
|
|
// Magento 1 URLs
|
|
html += '<div>';
|
|
html += '<h4 style="margin-bottom: 10px; color: #333; border-bottom: 2px solid #FF6B35; padding-bottom: 5px;">Magento 1 URLs (' + (storeData.m1_urls.length || 0) + ')</h4>';
|
|
if (storeData.m1_urls.length > 0) {
|
|
html += '<div style="background: #fff5f0; padding: 10px; border-radius: 4px; border-left: 3px solid #FF6B35;">';
|
|
storeData.m1_urls.forEach((urlData, index) => {
|
|
html += '<div style="margin-bottom: 10px; padding: 8px; background: white; border-radius: 4px; font-family: monospace; font-size: 0.9em;">';
|
|
html += '<div style="color: #333; margin-bottom: 3px;"><strong>URL ' + (index + 1) + ':</strong></div>';
|
|
html += '<div style="color: #E54A0F; word-break: break-all;">' + escapeHtml(urlData.url || '-') + '</div>';
|
|
if (urlData.target_path) {
|
|
html += '<div style="color: #666; font-size: 0.85em; margin-top: 3px;">Target: ' + escapeHtml(urlData.target_path) + '</div>';
|
|
}
|
|
html += '</div>';
|
|
});
|
|
html += '</div>';
|
|
} else {
|
|
html += '<div style="color: #999; font-style: italic; padding: 10px;">No URLs found in Magento 1</div>';
|
|
}
|
|
html += '</div>';
|
|
|
|
// Magento 2 URLs
|
|
html += '<div>';
|
|
html += '<h4 style="margin-bottom: 10px; color: #333; border-bottom: 2px solid #E54A0F; padding-bottom: 5px;">Magento 2 URLs (' + (storeData.m2_urls.length || 0) + ')</h4>';
|
|
if (storeData.m2_urls.length > 0) {
|
|
html += '<div style="background: #fff5f0; padding: 10px; border-radius: 4px; border-left: 3px solid #E54A0F;">';
|
|
storeData.m2_urls.forEach((urlData, index) => {
|
|
html += '<div style="margin-bottom: 10px; padding: 8px; background: white; border-radius: 4px; font-family: monospace; font-size: 0.9em;">';
|
|
html += '<div style="color: #333; margin-bottom: 3px;"><strong>URL ' + (index + 1) + ':</strong></div>';
|
|
html += '<div style="color: #E54A0F; word-break: break-all;">' + escapeHtml(urlData.url || '-') + '</div>';
|
|
if (urlData.target_path) {
|
|
html += '<div style="color: #666; font-size: 0.85em; margin-top: 3px;">Target: ' + escapeHtml(urlData.target_path) + '</div>';
|
|
}
|
|
html += '</div>';
|
|
});
|
|
html += '</div>';
|
|
} else {
|
|
html += '<div style="color: #999; font-style: italic; padding: 10px;">No URLs found in Magento 2</div>';
|
|
}
|
|
html += '</div>';
|
|
|
|
html += '</div>';
|
|
html += '</div>';
|
|
html += '</div>';
|
|
});
|
|
|
|
content.innerHTML = html;
|
|
}
|
|
|
|
// Allow Enter key to trigger search
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const skuFilter = document.getElementById('skuFilter');
|
|
if (skuFilter) {
|
|
skuFilter.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
compareUrls();
|
|
}
|
|
});
|
|
}
|
|
|
|
const singleSkuInput = document.getElementById('singleSkuInput');
|
|
if (singleSkuInput) {
|
|
singleSkuInput.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
compareSingleSku();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Fix / migrate product URLs from M1 to M2
|
|
window.fixProductUrls = async function fixProductUrls() {
|
|
const sku = document.getElementById('fixSkuInput').value.trim();
|
|
const dryRun = document.getElementById('fixDryRunInput').checked;
|
|
const btn = document.getElementById('fixUrlsBtn');
|
|
|
|
if (!dryRun && !confirm('This will INSERT URL rewrites into Magento 2. Continue?')) {
|
|
return;
|
|
}
|
|
|
|
btn.disabled = true;
|
|
btn.textContent = dryRun ? 'Previewing...' : 'Fixing...';
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
if (sku) formData.append('sku', sku);
|
|
formData.append('dry_run', dryRun ? '1' : '0');
|
|
|
|
const response = await fetch(window.productUrlsRoutes.fixUrls, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
'Accept': 'application/json'
|
|
},
|
|
body: formData
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!data.success) {
|
|
alert('Error: ' + (data.message || 'unknown'));
|
|
return;
|
|
}
|
|
|
|
document.getElementById('fixAdded').textContent = data.added;
|
|
document.getElementById('fixAddedLabel').textContent = data.dry_run ? 'Would Add' : 'Added';
|
|
document.getElementById('fixSkippedExisting').textContent = data.skipped_existing;
|
|
document.getElementById('fixSkippedNoProduct').textContent = data.skipped_no_m2_product;
|
|
document.getElementById('fixErrors').textContent = data.errors;
|
|
document.getElementById('fixLogOutput').textContent =
|
|
(data.log || []).join('\n') ||
|
|
`(no log lines — ${data.total_m1_rewrites} M1 rewrites scanned)`;
|
|
document.getElementById('fixResultsSection').style.display = 'block';
|
|
} catch (error) {
|
|
console.error('fixProductUrls error:', error);
|
|
alert('Failed: ' + error.message);
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Fix URLs';
|
|
}
|
|
};
|