Refactor URL pattern matching and improve image loading for product thumbnails

This commit is contained in:
2025-09-10 23:08:38 +02:00
parent ef15f16e18
commit 17d69ac9df

292
apilo.js
View File

@@ -4,163 +4,207 @@ const patterns = [
/^https:\/\/projectpro\.apilo\.com\/order\/order\/in-progress\/?$/,
/^https:\/\/projectpro\.apilo\.com\/order\/order\/to-send\/?$/,
/^https:\/\/projectpro\.apilo\.com\/order\/order\/completed\/?$/,
/^https:\/\/projectpro\.apilo\.com\/order\/order\/all\/?$/
/^https:\/\/projectpro\.apilo\.com\/order\/order\/status\/?$/,
/^https:\/\/projectpro\.apilo\.com\/order\/order\/(news|in-progress|to-send|completed|all)\/?$/,
/^https:\/\/projectpro\.apilo\.com\/order\/order\/status\/[^\/?#]+\/?$/
];
if (patterns.some(pattern => pattern.test(currentUrl))) { console.log('test');
if (patterns.some(pattern => pattern.test(currentUrl))) {
console.log('test');
waitForTableAndSetImage();
attachTableReloadListener();
} else {
console.log('Nie pasuje do żadnego wzorca URL');
}
// --- 2) Czekanie na tabelę i start przetwarzania ---
function waitForTableAndSetImage() {
const start = Date.now();
const maxWait = 10000; // 10s
const intervalId = setInterval(() => {
let dataTables_scrollBody = document.getElementsByClassName('dataTables_scrollBody');
if (dataTables_scrollBody.length > 0) {
let dataTables_tbody = dataTables_scrollBody[0].getElementsByTagName('tbody')[0];
let rows = dataTables_tbody.getElementsByTagName('tr');
if (rows.length > 0) {
clearInterval(intervalId);
setImageToProduct();
}
const tbody = document.querySelector('#DataTables_Table_0 tbody');
if (tbody && tbody.querySelector('tr')) {
clearInterval(intervalId);
setImageToProduct();
} else if (Date.now() - start > maxWait) {
clearInterval(intervalId);
}
}, 100);
}, 120);
}
// --- 3) Obserwator zmian w tabeli (przeładowanie/paginacja) ---
function attachTableReloadListener() {
const table = document.querySelector('.dataTables_scrollBody table');
if (table) {
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
waitForTableAndSetImage();
break;
}
const tbody = document.querySelector('#DataTables_Table_0 tbody');
if (!tbody) return;
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === 'childList') {
setImageToProduct();
break;
}
}
});
observer.observe(tbody, { childList: true, subtree: false });
}
// --- 4) Główny worker: wstawianie miniatur wg SKU ---
const imgCache = new Map(); // SKU -> imgURL
function setImageToProduct() {
const tbody = document.querySelector('#DataTables_Table_0 tbody');
if (!tbody) return;
const rows = Array.from(tbody.querySelectorAll('tr'));
rows.forEach(tr => {
const cells = tr.querySelectorAll('td');
if (cells.length < 4) return;
// Kolumna 2 zawiera info z domeną (wklejone w tym samym TD co numer zamówienia itp.)
const domain = detectDomainFromCell(cells[1]);
if (!domain) return;
// Kolumna 4: "Dane produktu"
const productsCell = cells[3];
// ============ POPRAWIONY FRAGMENT ============
// Każdy produkt: <div data-item="all-products"><div data-item="product">...</div></div>
const productBlocks = productsCell.querySelectorAll('[data-item="all-products"] [data-item="product"]');
productBlocks.forEach(block => {
// nie rób drugi raz
if (block.dataset.imgInserted === '1') return;
// Struktura: [data-item="product"] > .d-flex.mb-2 > [imgHolder DIV] + [dataHolder DIV]
const flex =
block.querySelector(':scope > .d-flex.mb-2') ||
block.querySelector('.d-flex.mb-2') ||
block.firstElementChild; // awaryjnie, gdyby klasy się zmieniły
if (!flex) return;
const imgHolder = flex.children?.[0]; // <div class="d-flex ... mr-3"><i .../></div>
const dataHolder = flex.children?.[1]; // <div> z nazwą, SKU, EAN, ceną
if (!imgHolder || !dataHolder) return;
// Jeśli już jest <img> w imgHolder, pomijamy
if (imgHolder.querySelector('img')) {
block.dataset.imgInserted = '1';
return;
}
// Znajdź konkretnie DIV z SKU (bez mieszania z innymi liniami)
const skuDiv = Array.from(dataHolder.querySelectorAll('div'))
.find(d => /SKU\s*:/i.test(d.textContent || ''));
const skuMatch = (skuDiv?.textContent || '').match(/SKU\s*:\s*([A-Za-z0-9_-]+)/i);
const sku = skuMatch?.[1]?.toUpperCase();
if (!sku) return;
const placeImage = (imgUrl) => {
if (!imgUrl) return;
// czyścimy ikonę <i> i wstawiamy miniaturę
imgHolder.innerHTML = '';
const imgEl = makeImg(imgUrl);
imgHolder.appendChild(imgEl);
setupPreview(imgEl, imgUrl);
block.dataset.imgInserted = '1';
};
if (imgCache.has(sku)) {
placeImage(imgCache.get(sku));
} else {
getProductData(sku, domain)
.then(url => {
if (url) imgCache.set(sku, url);
placeImage(url);
})
.catch(() => { });
}
});
observer.observe(table.querySelector('tbody'), { childList: true });
}
});
}
function setImageToProduct(img = '') {
let dataTables_scrollBody = document.getElementsByClassName('dataTables_scrollBody');
if (dataTables_scrollBody.length > 0) {
let dataTables_tbody = dataTables_scrollBody[0].getElementsByTagName('tbody')[0];
let rows = dataTables_tbody.getElementsByTagName('tr');
for (let i = 0; i < rows.length; i++) {
let cells = rows[i].getElementsByTagName('td');
if (cells.length > 1) {
let secondCellText = cells[1].textContent.trim();
let domain;
if (secondCellText.includes('marianek.pl')) {
domain = 'marianek.pl';
} else if (secondCellText.includes('pomysloweprezenty.pl')) {
domain = 'pomysloweprezenty.pl';
} else {
continue;
}
if (cells.length >= 5) {
let fifthCell = cells[4];
let divsInFifthCell = fifthCell.children;
for (let i = 0; i < divsInFifthCell.length; i++) {
let currentDiv = divsInFifthCell[i];
let imgDiv = currentDiv.getElementsByTagName('div')[0];
let dataDiv = currentDiv.getElementsByTagName('div')[1];
if (dataDiv) {
let skuText = dataDiv.innerHTML.match(/SKU:\s*([A-Za-z0-9-]+)/);
if (skuText && skuText[1]) {
getProductData(skuText[1], domain).then(data => {
if (!data) {
console.log('Product not found:', skuText[1]);
return;
}
console.log('Product found:', skuText[1]);
imgDiv.innerHTML = '';
const imgElement = makeImg(data);
imgDiv.appendChild(imgElement);
imgElement.addEventListener('mouseover', function() {
showLargeImage(data, imgElement);
});
imgElement.addEventListener('mouseout', function() {
hideLargeImage();
});
});
}
}
}
}
}
}
}
function detectDomainFromCell(td) {
const text = (td.textContent || '').toLowerCase();
if (text.includes('marianek.pl')) return 'marianek.pl';
if (text.includes('pomysloweprezenty.pl')) return 'pomysloweprezenty.pl';
return null;
}
function makeImg(src = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png') {
function makeImg(src) {
const img = document.createElement('img');
img.src = src;
img.alt = 'image';
img.alt = 'product';
img.className = 'img-fluid center-block';
img.style.maxWidth = '48px';
img.style.maxHeight = '48px';
img.style.objectFit = 'contain';
img.style.cursor = 'zoom-in';
return img;
}
function showLargeImage(src, imgElement) {
const largeImg = document.createElement('img');
largeImg.src = src;
largeImg.style.position = 'absolute';
largeImg.style.maxWidth = '400px';
largeImg.style.maxHeight = '400px';
largeImg.style.border = '1px solid #ccc';
largeImg.style.background = '#fff';
largeImg.style.zIndex = '1000';
largeImg.style.top = imgElement.getBoundingClientRect().top + window.scrollY + 'px';
largeImg.style.left = imgElement.getBoundingClientRect().left + imgElement.offsetWidth + 10 + 'px';
largeImg.id = 'largeImagePreview';
// --- 5) Podgląd dużego obrazka ---
function setupPreview(imgElement, src) {
let largeImg = null;
document.body.appendChild(largeImg);
}
function hideLargeImage() {
const largeImg = document.getElementById('largeImagePreview');
if (largeImg) {
largeImg.remove();
}
const onMove = (ev) => {
if (!largeImg) return;
const pad = 10;
largeImg.style.top = (ev.pageY - largeImg.height / 2) + 'px';
largeImg.style.left = (ev.pageX + pad) + 'px';
};
const onOver = (ev) => {
if (largeImg) return;
largeImg = document.createElement('img');
largeImg.id = 'largeImagePreview';
largeImg.src = src;
largeImg.style.position = 'absolute';
largeImg.style.maxWidth = '400px';
largeImg.style.maxHeight = '400px';
largeImg.style.border = '1px solid #ccc';
largeImg.style.background = '#fff';
largeImg.style.zIndex = '10000';
largeImg.style.pointerEvents = 'none';
document.body.appendChild(largeImg);
onMove(ev);
document.addEventListener('mousemove', onMove);
};
const onOut = () => {
if (largeImg) {
document.removeEventListener('mousemove', onMove);
largeImg.remove();
largeImg = null;
}
};
imgElement.addEventListener('mouseover', onOver);
imgElement.addEventListener('mouseout', onOut);
imgElement.addEventListener('mouseleave', onOut);
}
// --- 6) API: pobranie URL obrazka po SKU i domenie ---
async function getProductData(sku, domain) {
let url;
let url = null;
if (domain === 'marianek.pl') {
url = `https://marianek.pl/api/v1/product.php?sku=${sku}`;
url = `https://marianek.pl/api/v1/product.php?sku=${encodeURIComponent(sku)}`;
} else if (domain === 'pomysloweprezenty.pl') {
url = `https://pomysloweprezenty.pl/api/v1/product.php?sku=${sku}`;
url = `https://pomysloweprezenty.pl/api/v1/product.php?sku=${encodeURIComponent(sku)}`;
} else {
console.error('Unsupported domain:', domain);
return null;
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
const resp = await fetch(url, { credentials: 'omit' });
if (!resp.ok) {
throw new Error(`HTTP error! status: ${resp.status}`);
}
const data = await response.json(); console.log(data);
return data.img;
} catch (error) {
console.error('Error fetching product data: ' + url, error);
const data = await resp.json();
// zakładam, że API zwraca { img: "https://..." }
return data?.img || null;
} catch (err) {
console.error('Error fetching product data:', url, err);
return null;
}
}
@@ -223,7 +267,7 @@ async function handleShipment(method, dropoffPoint = null) {
if (dropoffPoint) {
await setDropoffPoint(dropoffPoint);
}
await setContent( method );
await setContent(method);
await setParcelWeight('1');
await submitShipment();
} catch (error) {
@@ -252,8 +296,8 @@ function retryUntilSuccess(fn, interval = 1000, retries = 10) {
});
}
function setContent( method ) {
if ( method == 'D2D.apaczka' || method == 'D2P.apaczka' || method == 'P2P.apaczka' || method == 'P2P.orlen' ) {
function setContent(method) {
if (method == 'D2D.apaczka' || method == 'D2P.apaczka' || method == 'P2P.apaczka' || method == 'P2P.orlen') {
return retryUntilSuccess(() => {
return new Promise((resolve, reject) => {
const selectElement = document.getElementById('warehousebundle_shipment_preferences_content');
@@ -271,7 +315,7 @@ function selectPackageType(method) {
return retryUntilSuccess(() => {
return new Promise((resolve, reject) => {
if ( method == 'P2P.orlen' ) {
if (method == 'P2P.orlen') {
//warehousebundle_shipment_carrierAccount
const selectElement = document.getElementById('warehousebundle_shipment_carrierAccount');
@@ -300,7 +344,7 @@ function selectPackageType(method) {
}, 1000);
}, 1000);
} else if ( method == 'D2P.apaczka' || method == 'P2P.apaczka' ) {
} else if (method == 'D2P.apaczka' || method == 'P2P.apaczka') {
//warehousebundle_shipment_carrierAccount
const selectElement = document.getElementById('warehousebundle_shipment_carrierAccount');
@@ -330,7 +374,7 @@ function selectPackageType(method) {
}, 1000);
} else if ( method == 'D2D.apaczka' ) {
} else if (method == 'D2D.apaczka') {
const selectElement = document.getElementById('warehousebundle_shipment_carrierAccount');
selectElement.value = 17;
@@ -375,7 +419,7 @@ function selectPackageType(method) {
}
function setShipmentMethod(method) {
if ( method == 'D2D.apaczka' || method == 'D2P.apaczka' || method == 'P2P.apaczka' || method == 'P2P.orlen' ) {
if (method == 'D2D.apaczka' || method == 'D2P.apaczka' || method == 'P2P.apaczka' || method == 'P2P.orlen') {
return new Promise((resolve, reject) => {
resolve();
});