update
This commit is contained in:
BIN
autoload/.DS_Store
vendored
Normal file
BIN
autoload/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
autoload/front/.DS_Store
vendored
Normal file
BIN
autoload/front/.DS_Store
vendored
Normal file
Binary file not shown.
614
devel.html
614
devel.html
@@ -1,468 +1,186 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="pl">
|
<html lang="pl">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" >
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<title>Geocode Cache Tester</title>
|
||||||
<title>[TITLE]</title>
|
<style>
|
||||||
<meta name="keywords" content="[META_KEYWORDS]">
|
:root { --gap: 12px; --radius: 12px; }
|
||||||
<meta name="description" content="[META_DESCRIPTION]">
|
* { box-sizing: border-box; }
|
||||||
[META_INDEX]
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; margin: 0; background: #0f172a; color: #e2e8f0; }
|
||||||
[CSS]
|
.wrap { max-width: 880px; margin: 40px auto; padding: 0 20px; }
|
||||||
[JAVA_SCRIPT]
|
h1 { font-size: 1.6rem; margin: 0 0 8px; }
|
||||||
</head>
|
p.lead { color: #94a3b8; margin: 0 0 24px; }
|
||||||
<body>
|
|
||||||
<div id="top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-sm-7">
|
|
||||||
[KONTENER:1]
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-5" id="mini-login">
|
|
||||||
[UZYTKOWNIK_MINI_LOGOWANIE]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="header">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-sm-4">
|
|
||||||
<a href="/" id="logo" title="">
|
|
||||||
<img src="/images/logo.svg" alt="">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-5">
|
|
||||||
<!--[WYSZUKIWARKA]-->
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-3">
|
|
||||||
[KOSZYK]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="main-menu">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
[MENU:1]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="mobile-menu-btn">
|
|
||||||
<i class="fa fa-bars"></i>
|
|
||||||
</div>
|
|
||||||
<div id="category-view">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-4 col-lg-3 left-column">
|
|
||||||
<div class="title">Kategorie</div>
|
|
||||||
[KATEGORIE]
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-8 col-lg-9">
|
|
||||||
[ZAWARTOSC]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="last-on-blog">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<div id="title">[PHP] global $lang; echo $lang['strefa-rodzica'];[/PHP]</div>
|
|
||||||
<div id="subtitle">[PHP] global $lang; echo ucfirst( $lang['najnowsze-porady'] );[/PHP]</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="ornament">
|
|
||||||
<img src="/images/ornament.svg" alt="">
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
[AKTUALNOSCI:9:3]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="promo-text">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
[KONTENER:2]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="footer">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
[KONTENER:3]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
[NEWSLETTER]
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-8">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
<div class="title">O firmie</div>
|
|
||||||
[MENU:2]
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
<div class="title">Dla klientów</div>
|
|
||||||
[MENU:3]
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
<div class="title">Moje konto</div>
|
|
||||||
[MENU:4]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
// ==UserScript==
|
form { display: flex; gap: var(--gap); flex-wrap: wrap; align-items: center; background: #111827; padding: 16px; border: 1px solid #1f2937; border-radius: var(--radius); box-shadow: 0 8px 30px rgba(0,0,0,.25) inset; }
|
||||||
// @name A
|
label { font-size: .95rem; color: #cbd5e1; }
|
||||||
// @version 1
|
input[type="text"] { flex: 1 1 420px; min-width: 260px; padding: 12px 14px; border-radius: 10px; border: 1px solid #334155; background: #0b1220; color: #e2e8f0; outline: none; }
|
||||||
// @grant none
|
input[type="text"]::placeholder { color: #64748b; }
|
||||||
// ==/UserScript==
|
button { padding: 12px 16px; border-radius: 10px; border: 1px solid #334155; background: #0b3b7a; color: #e2e8f0; cursor: pointer; }
|
||||||
|
button.secondary { background: #0b1220; }
|
||||||
|
button:disabled { opacity: .6; cursor: progress; }
|
||||||
|
|
||||||
window.onload = function() {
|
.status { margin: 14px 0 0; font-size: .95rem; color: #94a3b8; min-height: 1.2em; }
|
||||||
const currentUrl = window.location.href;
|
|
||||||
|
|
||||||
const patterns = [
|
.card { background: #0b1220; border: 1px solid #1f2937; border-radius: var(--radius); padding: 16px; margin-top: 18px; }
|
||||||
/^https:\/\/projectpro\.apilo\.com\/order\/order\/news\/?$/,
|
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||||
/^https:\/\/projectpro\.apilo\.com\/order\/order\/in-progress\/?$/,
|
.kv { background: #0a0f1a; border: 1px solid #1f2937; border-radius: 10px; padding: 12px; }
|
||||||
/^https:\/\/projectpro\.apilo\.com\/order\/order\/to-send\/?$/,
|
.kv b { color: #cbd5e1; display: inline-block; min-width: 160px; }
|
||||||
/^https:\/\/projectpro\.apilo\.com\/order\/order\/completed\/?$/,
|
.pill { display: inline-block; padding: 4px 10px; border-radius: 999px; border: 1px solid #334155; background: #0a0f1a; }
|
||||||
/^https:\/\/projectpro\.apilo\.com\/order\/order\/all\/?$/
|
.ok { color: #a7f3d0; }
|
||||||
];
|
.warn { color: #fbbf24; }
|
||||||
|
.err { color: #fca5a5; }
|
||||||
|
|
||||||
if (patterns.some(pattern => pattern.test(currentUrl))) {
|
details { margin-top: 10px; }
|
||||||
waitForTableAndSetImage();
|
pre { background: #010409; border: 1px solid #111827; padding: 12px; border-radius: 10px; overflow: auto; }
|
||||||
attachTableReloadListener();
|
|
||||||
|
.examples { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
|
.examples a { font-size: .9rem; text-decoration: none; color: #cbd5e1; border: 1px dashed #374151; padding: 6px 10px; border-radius: 999px; }
|
||||||
|
|
||||||
|
@media (max-width: 720px) { .grid { grid-template-columns: 1fr; } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>Tester geokodowania z cache (PHP + MySQL)</h1>
|
||||||
|
<p class="lead">Wpisz <b>adres</b> albo <b>"lat,lng"</b>. Front wywołuje <code>geocode-cache.php</code>, który najpierw sprawdza cache w MySQL, a dopiero potem pyta Google. W odpowiedzi zobaczysz, czy wynik pochodzi z cache.</p>
|
||||||
|
|
||||||
|
<form id="geo-form">
|
||||||
|
<label for="query">Adres lub współrzędne:</label>
|
||||||
|
<input id="query" type="text" placeholder="np. Stefana Okrzei 14, Brzeziny lub 51.7592,19.4550" required />
|
||||||
|
<button id="submit" type="submit">Geokoduj</button>
|
||||||
|
<button id="clear" type="button" class="secondary">Wyczyść</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="status" class="status"></div>
|
||||||
|
|
||||||
|
<div class="examples">
|
||||||
|
<span class="pill">Przykłady:</span>
|
||||||
|
<a href="#" data-q="Stefana Okrzei 14, Brzeziny">Brzeziny, Okrzei 14</a>
|
||||||
|
<a href="#" data-q="Rokicińska 130, Andrespol">Andrespol, Rokicińska 130</a>
|
||||||
|
<a href="#" data-q="52.2297,21.0122">52.2297,21.0122</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="result" class="card" hidden>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="kv"><b>Źródło</b> <span id="from_cache" class="pill"></span></div>
|
||||||
|
<div class="kv"><b>Adres sformatowany</b> <span id="formatted_address"></span></div>
|
||||||
|
<div class="kv"><b>Szerokość (lat)</b> <span id="lat"></span></div>
|
||||||
|
<div class="kv"><b>Długość (lng)</b> <span id="lng"></span></div>
|
||||||
|
<div class="kv"><b>Place ID</b> <span id="place_id"></span></div>
|
||||||
|
<div class="kv"><b>Dostawca</b> <span id="provider"></span></div>
|
||||||
|
</div>
|
||||||
|
<details>
|
||||||
|
<summary>Surowa odpowiedź JSON</summary>
|
||||||
|
<pre id="raw"></pre>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// >>> DOSTOSUJ TO POLE DO SWOJEJ ŚCIEŻKI BACKENDU <<<
|
||||||
|
const API_URL = '/geocode-cache.php';
|
||||||
|
|
||||||
|
const $ = (sel) => document.querySelector(sel);
|
||||||
|
const $$ = (sel) => Array.from(document.querySelectorAll(sel));
|
||||||
|
|
||||||
|
function isLatLng(str) {
|
||||||
|
return /^-?\d+(?:\.\d+)?\s*,\s*-?\d+(?:\.\d+)?$/.test(String(str).trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForTableAndSetImage() {
|
async function geocode(query) {
|
||||||
const intervalId = setInterval(() => {
|
const params = new URLSearchParams();
|
||||||
let dataTables_scrollBody = document.getElementsByClassName('dataTables_scrollBody');
|
if (isLatLng(query)) {
|
||||||
if (dataTables_scrollBody.length > 0) {
|
const [lat, lng] = String(query).split(',').map(v => parseFloat(v.trim()));
|
||||||
let dataTables_tbody = dataTables_scrollBody[0].getElementsByTagName('tbody')[0];
|
params.set('lat', lat);
|
||||||
let rows = dataTables_tbody.getElementsByTagName('tr');
|
params.set('lng', lng);
|
||||||
|
} else {
|
||||||
if (rows.length > 0) {
|
params.set('q', query);
|
||||||
clearInterval(intervalId); // Zatrzymanie dalszego wykonywania setInterval
|
|
||||||
setImageToProduct();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100); // Sprawdza co 100ms, można dostosować czas
|
|
||||||
}
|
|
||||||
|
|
||||||
function attachTableReloadListener() {
|
|
||||||
const table = document.querySelector('.dataTables_scrollBody table');
|
|
||||||
if (table) {
|
|
||||||
// Tworzymy obserwatora zmian w DOM
|
|
||||||
const observer = new MutationObserver((mutationsList, observer) => {
|
|
||||||
for (const mutation of mutationsList) {
|
|
||||||
if (mutation.type === 'childList') {
|
|
||||||
// Wykonaj kod po zmianie w tabeli (dodanie/usunięcie wierszy)
|
|
||||||
console.log('Table content changed');
|
|
||||||
waitForTableAndSetImage();
|
|
||||||
break; // Zatrzymujemy iterację po pierwszej znalezionej zmianie
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Konfiguracja obserwatora do nasłuchiwania zmian w zawartości tabeli
|
|
||||||
observer.observe(table.querySelector('tbody'), { childList: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const res = await fetch(API_URL + '?' + params.toString(), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Accept': 'application/json' },
|
||||||
|
cache: 'no-store',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error('HTTP ' + res.status);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
if (data && data.error) throw new Error(data.error);
|
||||||
|
if (!data || typeof data.lat !== 'number' || typeof data.lng !== 'number') {
|
||||||
|
throw new Error('Nieprawidłowa odpowiedź API');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setImageToProduct(img = '') {
|
function setStatus(text, type = '') {
|
||||||
let dataTables_scrollBody = document.getElementsByClassName('dataTables_scrollBody');
|
const el = $('#status');
|
||||||
if (dataTables_scrollBody.length > 0) {
|
el.textContent = text || '';
|
||||||
let dataTables_tbody = dataTables_scrollBody[0].getElementsByTagName('tbody')[0];
|
el.className = 'status ' + (type || '');
|
||||||
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);
|
|
||||||
|
|
||||||
// Dodanie eventu na hover
|
|
||||||
imgElement.addEventListener('mouseover', function() {
|
|
||||||
showLargeImage(data, imgElement);
|
|
||||||
});
|
|
||||||
|
|
||||||
imgElement.addEventListener('mouseout', function() {
|
|
||||||
hideLargeImage();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeImg(src = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png') {
|
function renderResult(data) {
|
||||||
const img = document.createElement('img');
|
$('#result').hidden = false;
|
||||||
img.src = src;
|
const pill = $('#from_cache');
|
||||||
img.alt = 'image';
|
pill.textContent = data.from_cache ? 'cache' + (data.stale ? ' (stare)' : '') : 'google';
|
||||||
img.className = 'img-fluid center-block';
|
pill.className = 'pill ' + (data.from_cache ? 'ok' : 'warn');
|
||||||
img.style.maxWidth = '48px';
|
$('#formatted_address').textContent = data.formatted_address || '\u2013';
|
||||||
img.style.maxHeight = '48px';
|
$('#lat').textContent = Number(data.lat).toFixed(6);
|
||||||
|
$('#lng').textContent = Number(data.lng).toFixed(6);
|
||||||
return img;
|
$('#place_id').textContent = data.place_id || '\u2013';
|
||||||
|
$('#provider').textContent = data.provider || '\u2013';
|
||||||
|
$('#raw').textContent = JSON.stringify(data, null, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLargeImage(src, imgElement) {
|
function clearResult() {
|
||||||
const largeImg = document.createElement('img');
|
$('#result').hidden = true;
|
||||||
largeImg.src = src;
|
$('#raw').textContent = '';
|
||||||
largeImg.style.position = 'absolute';
|
$('#formatted_address').textContent = '';
|
||||||
largeImg.style.maxWidth = '400px';
|
$('#lat').textContent = '';
|
||||||
largeImg.style.maxHeight = '400px';
|
$('#lng').textContent = '';
|
||||||
largeImg.style.border = '1px solid #ccc';
|
$('#place_id').textContent = '';
|
||||||
largeImg.style.background = '#fff';
|
$('#provider').textContent = '';
|
||||||
largeImg.style.zIndex = '1000';
|
$('#from_cache').textContent = '';
|
||||||
largeImg.style.top = imgElement.getBoundingClientRect().top + window.scrollY + 'px';
|
|
||||||
largeImg.style.left = imgElement.getBoundingClientRect().left + imgElement.offsetWidth + 10 + 'px';
|
|
||||||
largeImg.id = 'largeImagePreview';
|
|
||||||
|
|
||||||
document.body.appendChild(largeImg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideLargeImage() {
|
$('#geo-form').addEventListener('submit', async (e) => {
|
||||||
const largeImg = document.getElementById('largeImagePreview');
|
e.preventDefault();
|
||||||
if (largeImg) {
|
const btn = $('#submit');
|
||||||
largeImg.remove();
|
const q = $('#query').value.trim();
|
||||||
}
|
if (!q) return;
|
||||||
}
|
|
||||||
|
|
||||||
async function getProductData(sku, domain) {
|
clearResult();
|
||||||
let url;
|
setStatus('Szukam\u2026', '');
|
||||||
if (domain === 'marianek.pl') {
|
btn.disabled = true;
|
||||||
url = `https://marianek.pl/api/v1/product.php?sku=${sku}`;
|
try {
|
||||||
} else if (domain === 'pomysloweprezenty.pl') {
|
const data = await geocode(q);
|
||||||
url = `https://pomysloweprezenty.pl/api/v1/product.php?sku=${sku}`;
|
renderResult(data);
|
||||||
} else {
|
setStatus(data.from_cache ? 'Trafienie w cache \u2013 szybko i tanio \u26A1' : 'Świeży wynik z Google \u2013 zapisano do cache', data.from_cache ? 'ok' : 'warn');
|
||||||
console.error('Unsupported domain:', domain);
|
} catch (err) {
|
||||||
return null;
|
console.error(err);
|
||||||
}
|
setStatus('Błąd: ' + (err?.message || err), 'err');
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
$('#clear').addEventListener('click', () => {
|
||||||
const response = await fetch(url);
|
$('#query').value = '';
|
||||||
if (!response.ok) {
|
clearResult();
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
setStatus('');
|
||||||
}
|
$('#query').focus();
|
||||||
|
});
|
||||||
|
|
||||||
const data = await response.json(); console.log(data);
|
$$('.examples a').forEach(a => a.addEventListener('click', (e) => {
|
||||||
return data.img;
|
e.preventDefault();
|
||||||
} catch (error) {
|
const q = e.currentTarget.getAttribute('data-q');
|
||||||
console.error('Error fetching product data: ' + url, error);
|
$('#query').value = q;
|
||||||
return null;
|
$('#geo-form').dispatchEvent(new Event('submit'));
|
||||||
}
|
}));
|
||||||
}
|
|
||||||
|
|
||||||
const currentUrl = window.location.href;
|
// autofocus
|
||||||
const pattern = /^https:\/\/projectpro\.apilo\.com\/warehouse\/shipment\/new-for-order\/.+/;
|
$('#query').focus();
|
||||||
|
</script>
|
||||||
if (pattern.test(currentUrl)) {
|
</body>
|
||||||
const portletBody = document.querySelector('.kt-portlet__body');
|
</html>
|
||||||
if (portletBody) {
|
|
||||||
const buttonContainer = document.createElement('div');
|
|
||||||
buttonContainer.className = 'custom-button-container';
|
|
||||||
buttonContainer.style.display = 'flex';
|
|
||||||
buttonContainer.style.gap = '10px';
|
|
||||||
buttonContainer.style.marginBottom = '15px';
|
|
||||||
portletBody.parentNode.insertBefore(buttonContainer, portletBody);
|
|
||||||
|
|
||||||
const buttonP2D = createButton('Inpost P2D', '#007bff', 'P2D.inpost', 'RZE14N||RZE14N');
|
|
||||||
const buttonD2D = createButton('Inpost D2D', '#ff7800', 'D2D.inpostkurier');
|
|
||||||
const buttonD2P = createButton('Inpost D2P', '#28a745', 'D2P.inpost');
|
|
||||||
const buttonP2P = createButton('Inpost P2P', '#ffc107', 'P2P.inpost', 'RZE14N||RZE14N');
|
|
||||||
|
|
||||||
buttonContainer.appendChild(buttonP2D);
|
|
||||||
buttonContainer.appendChild(buttonD2D);
|
|
||||||
buttonContainer.appendChild(buttonD2P);
|
|
||||||
buttonContainer.appendChild(buttonP2P);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createButton(text, backgroundColor, method, dropoffPoint = null) {
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.textContent = text;
|
|
||||||
button.style.background = backgroundColor;
|
|
||||||
button.style.display = 'inline-flex';
|
|
||||||
button.style.width = '200px';
|
|
||||||
button.style.height = '40px';
|
|
||||||
button.style.alignItems = 'center';
|
|
||||||
button.style.justifyContent = 'center';
|
|
||||||
button.style.color = '#FFF';
|
|
||||||
button.style.border = 'none';
|
|
||||||
button.style.cursor = 'pointer';
|
|
||||||
|
|
||||||
button.addEventListener('click', () => handleShipment(method, dropoffPoint));
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleShipment(method, dropoffPoint = null) {
|
|
||||||
try {
|
|
||||||
await selectPackageType();
|
|
||||||
await setShipmentMethod(method);
|
|
||||||
if (dropoffPoint) {
|
|
||||||
await setDropoffPoint(dropoffPoint);
|
|
||||||
}
|
|
||||||
await setParcelWeight('1');
|
|
||||||
await submitShipment();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Wystąpił błąd: ', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function retryUntilSuccess(fn, interval = 500, retries = 10) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const attempt = async () => {
|
|
||||||
try {
|
|
||||||
const result = await fn();
|
|
||||||
resolve(result);
|
|
||||||
} catch (err) {
|
|
||||||
if (retries === 0) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
retries--;
|
|
||||||
attempt();
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
attempt();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectPackageType() {
|
|
||||||
return retryUntilSuccess(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const selectElement = document.getElementById('warehousebundle_shipment_packageType');
|
|
||||||
if (selectElement) {
|
|
||||||
selectElement.value = 'package';
|
|
||||||
const event = document.createEvent('HTMLEvents');
|
|
||||||
event.initEvent('change', true, false);
|
|
||||||
selectElement.dispatchEvent(event);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject('Nie znaleziono elementu packageType');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setShipmentMethod(method) {
|
|
||||||
return retryUntilSuccess(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const methodElement = document.getElementById('warehousebundle_shipment_method');
|
|
||||||
if (methodElement) {
|
|
||||||
methodElement.value = method;
|
|
||||||
const methodEvent = document.createEvent('HTMLEvents');
|
|
||||||
methodEvent.initEvent('change', true, false);
|
|
||||||
methodElement.dispatchEvent(methodEvent);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject('Nie znaleziono elementu shipment_method');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDropoffPoint(dropoffPoint) {
|
|
||||||
return retryUntilSuccess(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const dropoffPointElement = document.getElementById('warehousebundle_shipment_preferences_dropoffPoint');
|
|
||||||
if (dropoffPointElement) {
|
|
||||||
dropoffPointElement.value = dropoffPoint;
|
|
||||||
const dropoffPointEvent = document.createEvent('HTMLEvents');
|
|
||||||
dropoffPointEvent.initEvent('change', true, false);
|
|
||||||
dropoffPointElement.dispatchEvent(dropoffPointEvent);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject('Nie znaleziono elementu dropoffPoint');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setParcelWeight(weight) {
|
|
||||||
return retryUntilSuccess(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const weightElement = document.getElementById('warehousebundle_shipment_shipmentParcels_0_weight');
|
|
||||||
if (weightElement) {
|
|
||||||
weightElement.value = weight;
|
|
||||||
const weightEvent = document.createEvent('HTMLEvents');
|
|
||||||
weightEvent.initEvent('change', true, false);
|
|
||||||
weightElement.dispatchEvent(weightEvent);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject('Nie znaleziono elementu shipmentParcels_0_weight');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitShipment() {
|
|
||||||
return retryUntilSuccess(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const submitButton = document.getElementById('warehousebundle_shipment_buttons_submit');
|
|
||||||
if (submitButton) {
|
|
||||||
submitButton.click();
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject('Nie znaleziono przycisku submit');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
211
geocode-cache.php
Normal file
211
geocode-cache.php
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
<?php
|
||||||
|
// geocode-cache.php
|
||||||
|
|
||||||
|
// ==== KONFIGURACJA ====
|
||||||
|
const DB_DSN = 'mysql:host=localhost;dbname=host117523_shoppro;charset=utf8mb4';
|
||||||
|
const DB_USER = 'host117523_shoppro';
|
||||||
|
const DB_PASS = 'mhA9WCEXEnRfTtbN33hL';
|
||||||
|
|
||||||
|
// Klucz trzymaj w .env/konfiguracji, tu tylko przykład:
|
||||||
|
const GOOGLE_GEOCODE_KEY = 'AIzaSyD-1SOVhJXr6HREtfmMILvlmV-hml3nxUg';
|
||||||
|
|
||||||
|
// Domyślne opcje zapytań do Google – zawęź region/język wg potrzeb
|
||||||
|
const GOOGLE_REGION = 'pl';
|
||||||
|
const GOOGLE_LANGUAGE = 'pl';
|
||||||
|
|
||||||
|
// TTL cache w dniach
|
||||||
|
const CACHE_TTL_DAYS_FORWARD = 365;
|
||||||
|
const CACHE_TTL_DAYS_REVERSE = 365;
|
||||||
|
|
||||||
|
$ALLOWED_ORIGINS = [
|
||||||
|
'https://twoja-domena.pl',
|
||||||
|
'https://www.twoja-domena.pl',
|
||||||
|
'http://localhost:3000',
|
||||||
|
'https://shoppro.project-dc.pl',
|
||||||
|
'https://moodo.pl'
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==== NAGŁÓWKI/CORS ====
|
||||||
|
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||||
|
if ($origin && in_array($origin, $ALLOWED_ORIGINS, true)) {
|
||||||
|
header('Access-Control-Allow-Origin: ' . $origin);
|
||||||
|
header('Vary: Origin');
|
||||||
|
}
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
|
||||||
|
|
||||||
|
// ==== POMOCNICZE ====
|
||||||
|
function normalize_query(string $q): string {
|
||||||
|
$q = trim($q);
|
||||||
|
$q = preg_replace('/\s+/u', ' ', $q);
|
||||||
|
return mb_strtolower($q, 'UTF-8');
|
||||||
|
}
|
||||||
|
function now_mysql(): string { return date('Y-m-d H:i:s'); }
|
||||||
|
function add_days(string $date, int $days): string { return date('Y-m-d H:i:s', strtotime("$date +$days days")); }
|
||||||
|
|
||||||
|
// Budowa stabilnego klucza cache
|
||||||
|
function build_cache_key(?string $q, ?float $lat, ?float $lng): array {
|
||||||
|
if ($q !== null && $q !== '') {
|
||||||
|
$raw = $q;
|
||||||
|
$norm = normalize_query($q);
|
||||||
|
$mode = 'forward';
|
||||||
|
} else {
|
||||||
|
// reverse: lat,lng
|
||||||
|
$latR = round((float)$lat, 6);
|
||||||
|
$lngR = round((float)$lng, 6);
|
||||||
|
$raw = sprintf('%F,%F', $lat, $lng);
|
||||||
|
$norm = sprintf('latlng:%0.6f,%0.6f', $latR, $lngR);
|
||||||
|
$mode = 'reverse';
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'raw' => $raw,
|
||||||
|
'norm' => $norm,
|
||||||
|
'hash' => hash('sha256', $norm),
|
||||||
|
'mode' => $mode,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// DB
|
||||||
|
try {
|
||||||
|
$pdo = new PDO(DB_DSN, DB_USER, DB_PASS, [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => 'DB connection failed']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wejście
|
||||||
|
$q = isset($_GET['q']) ? trim((string)$_GET['q']) : '';
|
||||||
|
$lat = isset($_GET['lat']) ? (float)$_GET['lat'] : null;
|
||||||
|
$lng = isset($_GET['lng']) ? (float)$_GET['lng'] : null;
|
||||||
|
|
||||||
|
if ($q === '' && ($lat === null || $lng === null)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Provide q=address or lat & lng']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = build_cache_key($q, $lat, $lng);
|
||||||
|
$now = now_mysql();
|
||||||
|
|
||||||
|
// ===== 1) CACHE LOOKUP (forward i reverse) =====
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h AND (expires_at IS NULL OR expires_at > NOW()) LIMIT 1");
|
||||||
|
$stmt->execute([':h' => $key['hash']]);
|
||||||
|
if ($row = $stmt->fetch()) {
|
||||||
|
$pdo->prepare("UPDATE geocode_cache SET hits = hits + 1, updated_at = NOW() WHERE id = :id")->execute([':id' => $row['id']]);
|
||||||
|
echo json_encode([
|
||||||
|
'from_cache' => true,
|
||||||
|
'source' => 'cache',
|
||||||
|
'stale' => false,
|
||||||
|
'lat' => (float)$row['lat'],
|
||||||
|
'lng' => (float)$row['lng'],
|
||||||
|
'formatted_address' => $row['formatted_address'],
|
||||||
|
'place_id' => $row['place_id'],
|
||||||
|
'provider' => $row['provider'],
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 2) PYTANIE DO GOOGLE =====
|
||||||
|
function google_request(array $params): array {
|
||||||
|
$base = 'https://maps.googleapis.com/maps/api/geocode/json';
|
||||||
|
$params['key'] = GOOGLE_GEOCODE_KEY;
|
||||||
|
$params += ['language' => GOOGLE_LANGUAGE, 'region' => GOOGLE_REGION];
|
||||||
|
$url = $base . '?' . http_build_query($params);
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3]);
|
||||||
|
$res = curl_exec($ch); $err = curl_error($ch); curl_close($ch);
|
||||||
|
if ($res === false) throw new RuntimeException('cURL error: ' . $err);
|
||||||
|
$data = json_decode($res, true);
|
||||||
|
if (!is_array($data)) throw new RuntimeException('Invalid JSON from Google');
|
||||||
|
if (($data['status'] ?? '') !== 'OK' || empty($data['results'][0])) {
|
||||||
|
$status = $data['status'] ?? 'UNKNOWN';
|
||||||
|
throw new RuntimeException('Geocoding failed: ' . $status);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$data = ($key['mode'] === 'forward')
|
||||||
|
? google_request(['address' => $q])
|
||||||
|
: google_request(['latlng' => $lat . ',' . $lng]);
|
||||||
|
|
||||||
|
$top = $data['results'][0];
|
||||||
|
$latV = (float)$top['geometry']['location']['lat'];
|
||||||
|
$lngV = (float)$top['geometry']['location']['lng'];
|
||||||
|
$fmt = (string)($top['formatted_address'] ?? '');
|
||||||
|
$pid = (string)($top['place_id'] ?? '');
|
||||||
|
|
||||||
|
// ===== 3) ZAPIS DO CACHE (forward i reverse) =====
|
||||||
|
$ttlDays = ($key['mode'] === 'forward') ? CACHE_TTL_DAYS_FORWARD : CACHE_TTL_DAYS_REVERSE;
|
||||||
|
$expires = add_days($now, $ttlDays);
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO geocode_cache
|
||||||
|
(query_hash, query_raw, query_norm, lat, lng, formatted_address, place_id, provider, hits, created_at, updated_at, expires_at, raw_json)
|
||||||
|
VALUES (:h, :raw, :norm, :lat, :lng, :fmt, :pid, 'google', 1, :now, :now, :exp, :json)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
lat = VALUES(lat),
|
||||||
|
lng = VALUES(lng),
|
||||||
|
formatted_address = VALUES(formatted_address),
|
||||||
|
place_id = VALUES(place_id),
|
||||||
|
updated_at = VALUES(updated_at),
|
||||||
|
expires_at = VALUES(expires_at),
|
||||||
|
raw_json = VALUES(raw_json),
|
||||||
|
hits = hits + 1");
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
':h' => $key['hash'],
|
||||||
|
':raw' => $key['raw'],
|
||||||
|
':norm'=> $key['norm'],
|
||||||
|
':lat' => $latV,
|
||||||
|
':lng' => $lngV,
|
||||||
|
':fmt' => $fmt,
|
||||||
|
':pid' => $pid,
|
||||||
|
':now' => $now,
|
||||||
|
':exp' => $expires,
|
||||||
|
':json'=> json_encode($data, JSON_UNESCAPED_UNICODE),
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'from_cache' => false,
|
||||||
|
'source' => 'google',
|
||||||
|
'stale' => false,
|
||||||
|
'lat' => $latV,
|
||||||
|
'lng' => $lngV,
|
||||||
|
'formatted_address' => $fmt,
|
||||||
|
'place_id' => $pid,
|
||||||
|
'provider' => 'google',
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// ===== 4) Fallback: zwróć najświeższe co mamy (też dla reverse) =====
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM geocode_cache WHERE query_hash = :h LIMIT 1");
|
||||||
|
$stmt->execute([':h' => $key['hash']]);
|
||||||
|
if ($row = $stmt->fetch()) {
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode([
|
||||||
|
'from_cache' => true,
|
||||||
|
'source' => 'cache',
|
||||||
|
'stale' => true,
|
||||||
|
'lat' => (float)$row['lat'],
|
||||||
|
'lng' => (float)$row['lng'],
|
||||||
|
'formatted_address' => $row['formatted_address'],
|
||||||
|
'place_id' => $row['place_id'],
|
||||||
|
'provider' => $row['provider'],
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} catch (Throwable $ignored) {}
|
||||||
|
|
||||||
|
http_response_code(502);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user