listy, i inne funkcje

This commit is contained in:
Mateusz Gruszczyński
2025-09-22 14:01:57 +02:00
parent 0b221696d4
commit 1a423a8b92
8 changed files with 877 additions and 64 deletions

View File

@@ -0,0 +1,153 @@
(function () {
const tbody = document.querySelector('#produkty-body');
const celInput = document.querySelector('#cel');
const box = document.querySelector('#celSyncBox');
const msg = document.querySelector('#celSyncMsg');
const btn = document.querySelector('#btnApplyCelFromSum');
if (!tbody || !celInput || !box || !msg || !btn) return;
const EPS = 0.01; // tolerancja porównania
function parsePrice(raw) {
if (!raw) return NaN;
const s = String(raw).trim().replace(/\s+/g, '').replace(',', '.');
const n = Number(s);
return Number.isFinite(n) && n >= 0 ? n : NaN;
}
function getRows() {
return Array.from(tbody.querySelectorAll('tr'));
}
function computeSum() {
const rows = getRows();
let hasNamed = false;
let sumAll = 0; // suma ze wszystkich wierszy z nazwą i poprawną ceną
let sumToBuy = 0; // suma tylko z wierszy NIE oznaczonych jako "Kupione"
for (const tr of rows) {
const nameInput = tr.querySelector('input[name="item_nazwa[]"]');
const priceInput = tr.querySelector('input[name="item_cena[]"]');
const kupioneSwitch = tr.querySelector('.kupione-switch');
const name = nameInput ? nameInput.value.trim() : '';
if (!name) continue; // ignoruj puste wiersze bez nazwy
hasNamed = true;
const priceVal = priceInput ? parsePrice(priceInput.value) : NaN;
if (Number.isNaN(priceVal)) continue;
// zawsze dolicz do sumy wszystkich
sumAll += priceVal;
// do sumy do-kupienia tylko jeśli nie jest oznaczone jako kupione
if (!(kupioneSwitch && kupioneSwitch.checked)) {
sumToBuy += priceVal;
}
}
return { hasNamed, sumAll, sumToBuy };
}
function readCel() {
const v = parsePrice(celInput.value);
return Number.isNaN(v) ? null : v;
}
function formatPln(n) {
// Nie narzucamy locale prosto 2 miejsca
return n.toFixed(2);
}
function updateUI() {
const { hasNamed, sumAll, sumToBuy } = computeSum();
// Brak produktów (brak nazw) lub obie sumy = 0 → nic nie pokazuj
if (!hasNamed || (sumAll <= 0 && sumToBuy <= 0)) {
box.classList.add('d-none');
btn.classList.add('d-none');
box.classList.remove('alert-success', 'alert-info');
msg.textContent = '';
return;
}
const cel = readCel();
const target = sumToBuy; // porównujemy do kwoty POZOSTAŁE DO KUPIENIA
// Jeśli cel nie ustawiony lub NaN → zaproponuj ustawienie celu = sumToBuy
if (cel === null) {
box.classList.remove('d-none');
box.classList.remove('alert-success');
box.classList.add('alert-info');
// pokazujemy obie sumy w komunikacie
msg.innerHTML = `
<div>Wszystkie: <strong>${formatPln(sumAll)} PLN</strong> ·
Do kupienia: <strong>${formatPln(sumToBuy)} PLN</strong></div>
<div class="mt-1">Możesz ustawić <strong>cel</strong> na kwotę do kupienia.</div>
`;
btn.textContent = `Ustaw cel = ${formatPln(target)} PLN`;
btn.classList.remove('d-none');
return;
}
// Mamy cel — porównanie do sumy do-kupienia
if (Math.abs(cel - target) <= EPS) {
box.classList.remove('d-none');
box.classList.remove('alert-info');
box.classList.add('alert-success');
msg.innerHTML = `
Suma <em>do kupienia</em> (<strong>${formatPln(target)} PLN</strong>) jest równa celowi.
<div class="small text-muted mt-1">Wszystkie: ${formatPln(sumAll)} PLN · Do kupienia: ${formatPln(sumToBuy)} PLN</div>
`;
btn.classList.add('d-none');
} else {
box.classList.remove('d-none');
box.classList.remove('alert-success');
box.classList.add('alert-info');
msg.innerHTML = `
<div>Wszystkie: <strong>${formatPln(sumAll)} PLN</strong> ·
Do kupienia: <strong>${formatPln(sumToBuy)} PLN</strong></div>
<div class="mt-1">Cel: <strong>${formatPln(cel)} PLN</strong></div>
`;
btn.textContent = `Zaktualizuj cel do ${formatPln(target)} PLN`;
btn.classList.remove('d-none');
}
}
btn.addEventListener('click', (e) => {
e.preventDefault();
const { sumToBuy } = computeSum();
if (sumToBuy > 0) {
celInput.value = formatPln(sumToBuy);
celInput.dispatchEvent(new Event('input', { bubbles: true }));
celInput.dispatchEvent(new Event('change', { bubbles: true }));
updateUI();
}
});
// Reaguj na zmiany cen/nazw
tbody.addEventListener('input', (e) => {
const name = e.target.getAttribute('name');
if (name === 'item_nazwa[]' || name === 'item_cena[]') {
updateUI();
}
});
// Reaguj na zmiany celu
celInput.addEventListener('input', updateUI);
celInput.addEventListener('change', updateUI);
// Obserwuj dodawanie/usuwanie wierszy przez inne skrypty
const mo = new MutationObserver(() => updateUI());
mo.observe(tbody, { childList: true, subtree: true });
// Init po załadowaniu
document.addEventListener('DOMContentLoaded', updateUI);
// i jedno wywołanie na starcie (gdy DOMContentLoaded już był)
updateUI();
})();