listy, i inne funkcje
This commit is contained in:
153
static/js/kwoty_formularz.js
Normal file
153
static/js/kwoty_formularz.js
Normal 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();
|
||||
})();
|
73
static/js/produkty_formularz.js
Normal file
73
static/js/produkty_formularz.js
Normal file
@@ -0,0 +1,73 @@
|
||||
(function () {
|
||||
const body = document.querySelector('#produkty-body');
|
||||
const addBtn = document.querySelector('#add-row');
|
||||
const clearBtn = document.querySelector('#clear-empty');
|
||||
|
||||
if (!body) return;
|
||||
|
||||
function reindexHidden() {
|
||||
const rows = [...body.querySelectorAll('tr')];
|
||||
rows.forEach((tr, idx) => {
|
||||
const hidden = tr.querySelector('input[type="hidden"][name^="item_kupione_val_"]');
|
||||
if (hidden) hidden.name = `item_kupione_val_${idx}`;
|
||||
});
|
||||
}
|
||||
|
||||
function makeRow() {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td><input type="text" class="form-control" name="item_nazwa[]" placeholder="np. Karma Brit 10kg" required></td>
|
||||
<td><input type="url" class="form-control" name="item_link[]" placeholder="https://..."></td>
|
||||
<td><input type="text" inputmode="decimal" class="form-control text-end" name="item_cena[]" placeholder="0,00"></td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input kupione-switch" type="checkbox">
|
||||
<input type="hidden" name="item_kupione_val_TMP" value="0">
|
||||
<label class="form-check-label small">Do kupienia</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-light border remove-row" title="Usuń wiersz">✕</button>
|
||||
</td>`;
|
||||
return tr;
|
||||
}
|
||||
|
||||
body.addEventListener('change', (e) => {
|
||||
if (e.target.classList.contains('kupione-switch')) {
|
||||
const tr = e.target.closest('tr');
|
||||
const hidden = tr.querySelector('input[type="hidden"][name^="item_kupione_val_"]');
|
||||
const label = tr.querySelector('.form-check-label');
|
||||
if (hidden) hidden.value = e.target.checked ? '1' : '0';
|
||||
if (label) label.textContent = e.target.checked ? 'Kupione' : 'Do kupienia';
|
||||
}
|
||||
});
|
||||
|
||||
body.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('remove-row')) {
|
||||
e.preventDefault();
|
||||
const tr = e.target.closest('tr');
|
||||
tr.remove();
|
||||
reindexHidden();
|
||||
}
|
||||
});
|
||||
|
||||
addBtn?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
body.appendChild(makeRow());
|
||||
reindexHidden();
|
||||
});
|
||||
|
||||
clearBtn?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
[...body.querySelectorAll('tr')].forEach(tr => {
|
||||
const name = tr.querySelector('input[name="item_nazwa[]"]')?.value.trim();
|
||||
const link = tr.querySelector('input[name="item_link[]"]')?.value.trim();
|
||||
const cena = tr.querySelector('input[name="item_cena[]"]')?.value.trim();
|
||||
if (!name && !link && !cena) tr.remove();
|
||||
});
|
||||
reindexHidden();
|
||||
});
|
||||
|
||||
// startowa normalizacja nazw hiddenów (ważne w trybie edycji)
|
||||
reindexHidden();
|
||||
})();
|
26
static/js/transakcje.js
Normal file
26
static/js/transakcje.js
Normal file
@@ -0,0 +1,26 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const modalW = new bootstrap.Modal(document.getElementById('modalWplata'));
|
||||
const modalX = new bootstrap.Modal(document.getElementById('modalWydatek'));
|
||||
|
||||
// WPŁATA
|
||||
document.querySelectorAll('.btn-edit-wplata').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const form = document.getElementById('formWplata');
|
||||
form.action = btn.dataset.action;
|
||||
document.getElementById('wplataKwota').value = btn.dataset.kwota || '';
|
||||
document.getElementById('wplataOpis').value = btn.dataset.opis || '';
|
||||
modalW.show();
|
||||
});
|
||||
});
|
||||
|
||||
// WYDATEK
|
||||
document.querySelectorAll('.btn-edit-wydatek').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const form = document.getElementById('formWydatek');
|
||||
form.action = btn.dataset.action;
|
||||
document.getElementById('wydatekKwota').value = btn.dataset.kwota || '';
|
||||
document.getElementById('wydatekOpis').value = btn.dataset.opis || '';
|
||||
modalX.show();
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user