Refactor URL pattern matching and improve image loading for product thumbnails
This commit is contained in:
292
apilo.js
292
apilo.js
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user