UI cleanup

This commit is contained in:
2026-05-30 23:39:56 +02:00
parent 44c45f5410
commit fc831478de
9 changed files with 1313 additions and 561 deletions
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -432,6 +432,7 @@ def log_payment():
total = float(data.get('total', 0)) total = float(data.get('total', 0))
balance_before = float(data.get('balance_before', 0)) balance_before = float(data.get('balance_before', 0))
balance_after = float(data.get('balance_after', 0)) balance_after = float(data.get('balance_after', 0))
buyer = (data.get('buyer') or '').strip()
cart_items = data.get('cart_items', []) # [{id, name, price, qty, image}] cart_items = data.get('cart_items', []) # [{id, name, price, qty, image}]
# Deduct stock for each sold item # Deduct stock for each sold item
@@ -447,7 +448,7 @@ def log_payment():
# Update balance atomically with payment log # Update balance atomically with payment log
data_manager.set_balance(balance_after) data_manager.set_balance(balance_after)
if data_manager.log_payment(items, total, balance_before, balance_after): if data_manager.log_payment(items, total, balance_before, balance_after, buyer):
return jsonify({'success': True}) return jsonify({'success': True})
return jsonify({'success': False, 'message': 'Error logging payment'}), 400 return jsonify({'success': False, 'message': 'Error logging payment'}), 400
except Exception as e: except Exception as e:
+207
View File
@@ -7,5 +7,212 @@
"scan_result": "MANUAL", "scan_result": "MANUAL",
"reason": "Unregistered", "reason": "Unregistered",
"timestamp": "2026-02-12T13:34:11.034695" "timestamp": "2026-02-12T13:34:11.034695"
},
{
"id": 2,
"card_uid": null,
"key_uid": null,
"name": "test",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:26.155886"
},
{
"id": 3,
"card_uid": null,
"key_uid": null,
"name": "teste",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:33.908972"
},
{
"id": 4,
"card_uid": null,
"key_uid": null,
"name": "testeste",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:36.542642"
},
{
"id": 5,
"card_uid": null,
"key_uid": null,
"name": "testst",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:39.726511"
},
{
"id": 6,
"card_uid": null,
"key_uid": null,
"name": "etsetset",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:42.367083"
},
{
"id": 7,
"card_uid": null,
"key_uid": null,
"name": "etsetst",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:44.990438"
},
{
"id": 8,
"card_uid": null,
"key_uid": null,
"name": "testestest",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:47.949316"
},
{
"id": 9,
"card_uid": null,
"key_uid": null,
"name": "testestes",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:50.328739"
},
{
"id": 10,
"card_uid": null,
"key_uid": null,
"name": "setsettes",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:53.062096"
},
{
"id": 11,
"card_uid": null,
"key_uid": null,
"name": "setests",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:55.647214"
},
{
"id": 12,
"card_uid": null,
"key_uid": null,
"name": "esttset",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T22:59:58.394528"
},
{
"id": 13,
"card_uid": null,
"key_uid": null,
"name": "etestest",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:00.890154"
},
{
"id": 14,
"card_uid": null,
"key_uid": null,
"name": "etsetset",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:03.952926"
},
{
"id": 15,
"card_uid": null,
"key_uid": null,
"name": "settet",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:07.040332"
},
{
"id": 16,
"card_uid": null,
"key_uid": null,
"name": "tetetet",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:09.693125"
},
{
"id": 17,
"card_uid": null,
"key_uid": null,
"name": "tetete",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:12.375991"
},
{
"id": 18,
"card_uid": null,
"key_uid": null,
"name": "tetetet",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:15.544080"
},
{
"id": 19,
"card_uid": null,
"key_uid": null,
"name": "eeee",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:17.805205"
},
{
"id": 20,
"card_uid": null,
"key_uid": null,
"name": "eeeee",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:39.566745"
},
{
"id": 21,
"card_uid": null,
"key_uid": null,
"name": "eeee",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:42.970896"
},
{
"id": 22,
"card_uid": null,
"key_uid": null,
"name": "eeeee",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:52.390520"
},
{
"id": 23,
"card_uid": null,
"key_uid": null,
"name": "wwww",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:00:55.606973"
},
{
"id": 24,
"card_uid": null,
"key_uid": null,
"name": "ewqewqeqw",
"scan_result": "MANUAL",
"reason": "Unregistered",
"timestamp": "2026-05-30T23:07:00.557343"
} }
] ]
+100 -5
View File
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -309,7 +309,7 @@ class DataManager:
print(f"Error deleting item: {e}") print(f"Error deleting item: {e}")
return False return False
def log_payment(self, items: list, total: float, balance_before: float, balance_after: float) -> bool: def log_payment(self, items: list, total: float, balance_before: float, balance_after: float, buyer: str = '') -> bool:
try: try:
store = self.load_store() store = self.load_store()
payments = store.get('payments', []) payments = store.get('payments', [])
@@ -317,6 +317,7 @@ class DataManager:
payments.append({ payments.append({
'id': new_id, 'id': new_id,
'timestamp': datetime.now().isoformat(), 'timestamp': datetime.now().isoformat(),
'buyer': buyer,
'items': items, 'items': items,
'total': round(total, 2), 'total': round(total, 2),
'balance_before': round(balance_before, 2), 'balance_before': round(balance_before, 2),
+600 -424
View File
File diff suppressed because it is too large Load Diff
+254 -19
View File
@@ -1,6 +1,11 @@
let editingParticipantId = null; let editingParticipantId = null;
let isAdmin = false; let isAdmin = false;
function fmtDate(iso) {
const [y, m, d] = iso.split('-');
return `${d}-${m}-${y.slice(2)}`;
}
// Initialize on page load // Initialize on page load
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
checkAdminStatus(); checkAdminStatus();
@@ -412,8 +417,8 @@ async function refreshLogs() {
filteredLogs.forEach(log => { filteredLogs.forEach(log => {
const row = document.createElement('tr'); const row = document.createElement('tr');
const timestamp = log.timestamp; const timestamp = log.timestamp;
const date = timestamp.split('T')[0]; const date = fmtDate(timestamp.split('T')[0]);
const time = timestamp.split('T')[1].substring(0, 8); const time = timestamp.split('T')[1].substring(0, 5);
// Determine status class // Determine status class
let statusClass = 'pass'; let statusClass = 'pass';
@@ -728,7 +733,14 @@ function switchTab(tabName, event) {
// Load tab-specific data // Load tab-specific data
if (tabName === 'documents') loadDocuments(); if (tabName === 'documents') loadDocuments();
if (tabName === 'store') loadStore(); if (tabName === 'store') {
// Always return to shop view when switching to store tab
storeViewIsHistory = false;
document.getElementById('storeShopView').style.display = '';
document.getElementById('storeHistoryView').style.display = 'none';
document.getElementById('storeViewToggleBtn').textContent = 'Zgodovina plačil';
loadStore();
}
} }
// Close modal when clicking outside // Close modal when clicking outside
@@ -746,6 +758,10 @@ window.onclick = function(event) {
if (event.target === addUnregisteredModal) closeAddUnregisteredModal(); if (event.target === addUnregisteredModal) closeAddUnregisteredModal();
if (event.target === addItemModal) closeAddItemModal(); if (event.target === addItemModal) closeAddItemModal();
if (event.target === editBalanceModal) closeEditBalanceModal(); if (event.target === editBalanceModal) closeEditBalanceModal();
const checkoutModal = document.getElementById('checkoutModal');
const monthlyModal = document.getElementById('monthlyModal');
if (event.target === checkoutModal) closeCheckoutModal();
if (event.target === monthlyModal) closeMonthlyOverview();
} }
// ===== LAST SCAN RESULT ===== // ===== LAST SCAN RESULT =====
@@ -909,9 +925,10 @@ function renderInventory() {
storeItems.forEach(item => { storeItems.forEach(item => {
const card = document.createElement('div'); const card = document.createElement('div');
card.className = 'inventory-card' + (item.stock === 0 ? ' out-of-stock' : ''); card.className = 'inventory-card' + (item.stock === 0 ? ' out-of-stock' : '');
const imgHtml = item.image const imgHtml = `<div class="inventory-card-img-wrap">${item.image
? `<img src="${item.image}" alt="${item.name}" class="inventory-card-img">` ? `<img src="${item.image}" alt="${item.name}" class="inventory-card-img">`
: `<div class="inventory-card-img-placeholder">${ITEM_FALLBACK_SVG}</div>`; : `<div class="inventory-card-img-placeholder">${ITEM_FALLBACK_SVG}</div>`
}</div>`;
card.innerHTML = ` card.innerHTML = `
${imgHtml} ${imgHtml}
<div class="inventory-card-body"> <div class="inventory-card-body">
@@ -1008,16 +1025,40 @@ function updateCartTotals() {
} }
} }
async function processPayment() { function processPayment() {
if (cart.length === 0) { if (cart.length === 0) {
alert('Košarica je prazna'); alert('Košarica je prazna');
return; return;
} }
const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0); const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0);
const balanceAfter = currentBalance + total; const balanceAfter = currentBalance + total;
if (!confirm(`Skupaj: €${total.toFixed(2)}\nStanje po plačilu: €${balanceAfter.toFixed(2)}\n\nPotrdite plačilo?`)) return; document.getElementById('checkoutFirstName').value = '';
document.getElementById('checkoutLastName').value = '';
document.getElementById('checkoutTotal').textContent = '€' + total.toFixed(2);
document.getElementById('checkoutBalanceAfter').textContent = '€' + balanceAfter.toFixed(2);
document.getElementById('checkoutModal').classList.add('show');
document.getElementById('checkoutFirstName').focus();
}
function closeCheckoutModal() {
document.getElementById('checkoutModal').classList.remove('show');
}
async function confirmCheckout() {
const firstName = document.getElementById('checkoutFirstName').value.trim();
const lastName = document.getElementById('checkoutLastName').value.trim();
if (!firstName || !lastName) {
alert('Prosimo, vnesite ime in priimek kupca');
return;
}
const buyer = firstName + ' ' + lastName;
const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0);
const balanceAfter = currentBalance + total;
closeCheckoutModal();
try { try {
const cartItems = cart.map(cartItem => { const cartItems = cart.map(cartItem => {
@@ -1036,6 +1077,7 @@ async function processPayment() {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
buyer,
items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })), items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })),
cart_items: cartItems, cart_items: cartItems,
total, total,
@@ -1047,9 +1089,18 @@ async function processPayment() {
const data = await response.json(); const data = await response.json();
if (!data.success) { alert('Napaka: ' + data.message); return; } if (!data.success) { alert('Napaka: ' + data.message); return; }
const payment = {
buyer,
items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })),
total,
balance_before: currentBalance,
balance_after: balanceAfter,
timestamp: new Date().toISOString()
};
cart = []; cart = [];
await loadStore(); await loadStore();
alert('Plačilo uspešno!'); printReceipt(payment);
} catch (error) { } catch (error) {
alert('Napaka: ' + error.message); alert('Napaka: ' + error.message);
} }
@@ -1212,15 +1263,18 @@ async function saveBalance() {
} }
// ===== PAYMENT HISTORY ===== // ===== PAYMENT HISTORY =====
let paymentHistoryVisible = false; let storeViewIsHistory = false;
function togglePaymentHistory() { function toggleStoreView() {
paymentHistoryVisible = !paymentHistoryVisible; storeViewIsHistory = !storeViewIsHistory;
const panel = document.getElementById('paymentHistoryPanel'); document.getElementById('storeShopView').style.display = storeViewIsHistory ? 'none' : '';
panel.style.display = paymentHistoryVisible ? 'block' : 'none'; document.getElementById('storeHistoryView').style.display = storeViewIsHistory ? '' : 'none';
if (paymentHistoryVisible) loadPaymentHistory(); document.getElementById('storeViewToggleBtn').textContent = storeViewIsHistory ? '← Nazaj v trgovino' : 'Zgodovina plačil';
if (storeViewIsHistory) loadPaymentHistory();
} }
function togglePaymentHistory() { toggleStoreView(); }
async function loadPaymentHistory() { async function loadPaymentHistory() {
try { try {
const response = await fetch('/api/store/payments'); const response = await fetch('/api/store/payments');
@@ -1232,25 +1286,27 @@ async function loadPaymentHistory() {
if (clearBtn) clearBtn.style.display = isAdmin ? 'inline-block' : 'none'; if (clearBtn) clearBtn.style.display = isAdmin ? 'inline-block' : 'none';
if (payments.length === 0) { if (payments.length === 0) {
tbody.innerHTML = `<tr><td colspan="7" style="text-align:center;padding:20px;color:var(--text-secondary);">Ni zabeleženih plačil</td></tr>`; tbody.innerHTML = `<tr><td colspan="9" style="text-align:center;padding:20px;color:var(--text-secondary);">Ni zabeleženih plačil</td></tr>`;
return; return;
} }
tbody.innerHTML = ''; tbody.innerHTML = '';
[...payments].reverse().forEach(p => { [...payments].reverse().forEach(p => {
const ts = p.timestamp; const ts = p.timestamp;
const date = ts.split('T')[0]; const date = fmtDate(ts.split('T')[0]);
const time = ts.split('T')[1].substring(0, 8); const time = ts.split('T')[1].substring(0, 5);
const itemsSummary = p.items.map(i => `${i.name} ×${i.qty}`).join(', '); const itemsSummary = p.items.map(i => `${i.name} ×${i.qty}`).join(', ');
const row = document.createElement('tr'); const row = document.createElement('tr');
row.innerHTML = ` row.innerHTML = `
<td>${p.id}</td> <td>${p.id}</td>
<td style="font-weight:600;">${p.buyer || '-'}</td>
<td>${date}</td> <td>${date}</td>
<td>${time}</td> <td>${time}</td>
<td style="max-width:220px;word-break:break-word;">${itemsSummary}</td> <td style="max-width:200px;word-break:break-word;">${itemsSummary}</td>
<td style="font-weight:700;color:var(--accent-blue);">€${p.total.toFixed(2)}</td> <td style="font-weight:700;color:var(--accent-blue);">€${p.total.toFixed(2)}</td>
<td>€${p.balance_before.toFixed(2)}</td> <td>€${p.balance_before.toFixed(2)}</td>
<td>€${p.balance_after.toFixed(2)}</td> <td>€${p.balance_after.toFixed(2)}</td>
<td><button class="action-btn edit-btn" onclick="printReceipt(${JSON.stringify(p).replace(/"/g, '&quot;')})">🖨 Natisni</button></td>
`; `;
tbody.appendChild(row); tbody.appendChild(row);
}); });
@@ -1259,6 +1315,185 @@ async function loadPaymentHistory() {
} }
} }
// ===== RECEIPT PRINT =====
const LOGO_URL = '/static/assets/logo.png';
function printReceipt(payment) {
const ts = payment.timestamp || new Date().toISOString();
const date = fmtDate(ts.split('T')[0]);
const time = ts.split('T')[1] ? ts.split('T')[1].substring(0, 5) : '';
const rows = payment.items.map(i =>
`<tr><td class="rp-td-name">${i.name}</td><td class="rp-td-qty">${i.qty}×</td><td class="rp-td-price">€${i.price.toFixed(2)}</td><td class="rp-td-sub">€${(i.price * i.qty).toFixed(2)}</td></tr>`
).join('');
const html = `
<div class="rp-page">
<div class="rp-logo-block">
<img src="${LOGO_URL}" alt="Logo" class="rp-logo" onerror="this.style.display='none'">
</div>
<div class="rp-divider"></div>
<div class="rp-title">POTRDILO O PLAČILU</div>
<div class="rp-meta">
<span>${date} ${time}</span>
<span>Prodajalec: <strong>${payment.buyer || '-'}</strong></span>
</div>
<div class="rp-divider-dashed"></div>
<table class="rp-table">
<thead><tr><th>Artikel</th><th>Kol.</th><th>Cena</th><th style="text-align:right;">Znesek</th></tr></thead>
<tbody>${rows}</tbody>
</table>
<div class="rp-divider"></div>
<div class="rp-total">
<span>SKUPAJ</span>
<span>€${payment.total.toFixed(2)}</span>
</div>
<div class="rp-divider-dashed"></div>
<div class="rp-footer">Hvala za nakup!</div>
</div>
`;
const area = document.getElementById('receiptPrintArea');
area.innerHTML = html;
area.style.display = 'block';
document.body.classList.add('printing-receipt');
window.print();
document.body.classList.remove('printing-receipt');
area.style.display = 'none';
}
// ===== MONTHLY OVERVIEW =====
let allPaymentsCache = [];
function openMonthlyOverview() {
const now = new Date();
const ym = now.toISOString().substring(0, 7);
document.getElementById('monthlyPickerInput').value = ym;
document.getElementById('monthlyModal').classList.add('show');
renderMonthlyOverview();
}
function closeMonthlyOverview() {
document.getElementById('monthlyModal').classList.remove('show');
}
async function renderMonthlyOverview() {
const picker = document.getElementById('monthlyPickerInput').value;
if (!picker) return;
try {
const response = await fetch('/api/store/payments');
allPaymentsCache = await response.json();
} catch (e) { return; }
const filtered = allPaymentsCache.filter(p => p.timestamp.startsWith(picker));
const content = document.getElementById('monthlyOverviewContent');
if (filtered.length === 0) {
content.innerHTML = '<p style="color:var(--text-secondary);padding:16px 0;">Ni plačil v izbranem mesecu.</p>';
return;
}
const grandTotal = filtered.reduce((s, p) => s + p.total, 0);
// Per-item breakdown
const itemTotals = {};
filtered.forEach(p => p.items.forEach(i => {
if (!itemTotals[i.name]) itemTotals[i.name] = { qty: 0, revenue: 0 };
itemTotals[i.name].qty += i.qty;
itemTotals[i.name].revenue += i.price * i.qty;
}));
const itemRows = Object.entries(itemTotals).map(([name, v]) =>
`<tr><td>${name}</td><td style="text-align:center;">${v.qty}</td><td style="text-align:right;font-weight:600;">€${v.revenue.toFixed(2)}</td></tr>`
).join('');
const txRows = [...filtered].reverse().map(p => {
const date = fmtDate(p.timestamp.split('T')[0]);
const time = p.timestamp.split('T')[1].substring(0, 5);
return `<tr><td>${date}</td><td>${time}</td><td style="font-weight:600;">${p.buyer || '-'}</td><td>${p.items.map(i => `${i.name} ×${i.qty}`).join(', ')}</td><td style="text-align:right;font-weight:700;color:var(--accent-blue);">€${p.total.toFixed(2)}</td></tr>`;
}).join('');
content.innerHTML = `
<div class="monthly-summary-total">Skupaj prihodek: <strong>€${grandTotal.toFixed(2)}</strong> (${filtered.length} transakcij)</div>
<h4 class="monthly-section-title">Po artiklih</h4>
<div class="table-container" style="margin-bottom:16px;">
<table><thead><tr><th>Artikel</th><th style="text-align:center;">Količina</th><th style="text-align:right;">Prihodek</th></tr></thead>
<tbody>${itemRows}</tbody></table>
</div>
<h4 class="monthly-section-title">Vse transakcije</h4>
<div class="table-container">
<table><thead><tr><th>Datum</th><th>Čas</th><th>Prodajalec</th><th>Artikli</th><th style="text-align:right;">Skupaj</th></tr></thead>
<tbody>${txRows}</tbody></table>
</div>
`;
}
function printMonthlyOverview() {
const picker = document.getElementById('monthlyPickerInput').value;
if (!picker || !allPaymentsCache.length) return;
const filtered = allPaymentsCache.filter(p => p.timestamp.startsWith(picker));
if (filtered.length === 0) { alert('Ni podatkov za izbrani mesec.'); return; }
const grandTotal = filtered.reduce((s, p) => s + p.total, 0);
const itemTotals = {};
filtered.forEach(p => p.items.forEach(i => {
if (!itemTotals[i.name]) itemTotals[i.name] = { qty: 0, revenue: 0 };
itemTotals[i.name].qty += i.qty;
itemTotals[i.name].revenue += i.price * i.qty;
}));
const itemRows = Object.entries(itemTotals).map(([name, v]) =>
`<tr><td>${name}</td><td style="text-align:center;">${v.qty}</td><td style="text-align:right;font-weight:600;">€${v.revenue.toFixed(2)}</td></tr>`
).join('');
const txRows = [...filtered].reverse().map(p => {
const date = fmtDate(p.timestamp.split('T')[0]);
const time = p.timestamp.split('T')[1].substring(0, 5);
const items = p.items.map(i => `${i.name} ×${i.qty} (€${(i.price*i.qty).toFixed(2)})`).join(', ');
return `<tr><td>${date}</td><td>${time}</td><td style="font-weight:600;">${p.buyer || '-'}</td><td>${items}</td><td style="text-align:right;font-weight:700;">€${p.total.toFixed(2)}</td></tr>`;
}).join('');
const [year, month] = picker.split('-');
const monthName = new Date(year, month - 1, 1).toLocaleDateString('sl-SI', { month: 'long', year: 'numeric' });
const printDate = fmtDate(new Date().toISOString().split('T')[0]);
const html = `
<div class="rp-page rp-monthly">
<div class="rp-logo-block">
<img src="${LOGO_URL}" alt="Logo" class="rp-logo" onerror="this.style.display='none'">
</div>
<div class="rp-divider"></div>
<div class="rp-title">MESEČNI PREGLED</div>
<div class="rp-meta"><span>${monthName}</span><span>Natisnjeno: ${printDate}</span></div>
<div class="rp-divider-dashed"></div>
<div class="rp-monthly-kpi">
<div class="rp-kpi-box"><div class="rp-kpi-label">Skupaj prihodek</div><div class="rp-kpi-value">€${grandTotal.toFixed(2)}</div></div>
<div class="rp-kpi-box"><div class="rp-kpi-label">Št. transakcij</div><div class="rp-kpi-value">${filtered.length}</div></div>
</div>
<div class="rp-section-title">Po artiklih</div>
<table class="rp-table">
<thead><tr><th>Artikel</th><th style="text-align:center;">Količina</th><th style="text-align:right;">Prihodek</th></tr></thead>
<tbody>${itemRows}</tbody>
</table>
<div class="rp-section-title" style="margin-top:16px;">Vse transakcije</div>
<table class="rp-table">
<thead><tr><th>Datum</th><th>Čas</th><th>Prodajalec</th><th>Artikli</th><th style="text-align:right;">Skupaj</th></tr></thead>
<tbody>${txRows}</tbody>
</table>
</div>
`;
const area = document.getElementById('receiptPrintArea');
area.innerHTML = html;
area.style.display = 'block';
document.body.classList.add('printing-receipt');
window.print();
document.body.classList.remove('printing-receipt');
area.style.display = 'none';
}
async function clearPaymentHistory() { async function clearPaymentHistory() {
if (!confirm('Izbriši celotno zgodovino plačil?')) return; if (!confirm('Izbriši celotno zgodovino plačil?')) return;
try { try {
+148 -111
View File
@@ -52,7 +52,7 @@
<div class="right-panel"> <div class="right-panel">
<!-- Tab Navigation --> <!-- Tab Navigation -->
<div class="tab-nav"> <div class="tab-nav">
<button class="tab-nav-btn active" onclick="switchTab('logs', event)">Dnevnik skeniranja</button> <button class="tab-nav-btn active" onclick="switchTab('logs', event)">Dnevnik</button>
<button class="tab-nav-btn" onclick="switchTab('participants', event)">Člani</button> <button class="tab-nav-btn" onclick="switchTab('participants', event)">Člani</button>
<button class="tab-nav-btn" onclick="switchTab('documents', event)">Dokumenti</button> <button class="tab-nav-btn" onclick="switchTab('documents', event)">Dokumenti</button>
<button class="tab-nav-btn" onclick="switchTab('store', event)">Trgovina</button> <button class="tab-nav-btn" onclick="switchTab('store', event)">Trgovina</button>
@@ -60,9 +60,11 @@
<!-- Participants Tab --> <!-- Participants Tab -->
<div id="participants-tab" class="tab-panel"> <div id="participants-tab" class="tab-panel">
<div class="panel-header"> <div class="toolbar">
<h2>Člani</h2> <span class="toolbar-title">Člani</span>
<button class="btn btn-primary" onclick="openAddParticipantModal()">Dodaj udeleženca</button> <div class="toolbar-actions">
<button class="tb-btn tb-btn-primary" onclick="openAddParticipantModal()">+ Dodaj udeleženca</button>
</div>
</div> </div>
<div class="panel-content"> <div class="panel-content">
<div class="table-container"> <div class="table-container">
@@ -78,8 +80,7 @@
<th>Dejanja</th> <th>Dejanja</th>
</tr> </tr>
</thead> </thead>
<tbody id="participantsTable"> <tbody id="participantsTable"></tbody>
</tbody>
</table> </table>
</div> </div>
</div> </div>
@@ -87,49 +88,43 @@
<!-- Scan Logs Tab --> <!-- Scan Logs Tab -->
<div id="logs-tab" class="tab-panel active"> <div id="logs-tab" class="tab-panel active">
<div class="panel-header"> <div class="toolbar">
<h2>Zgodovina skeniranja</h2> <div class="toolbar-filters">
<div class="header-actions"> <div class="tf-group">
<button class="btn btn-primary" onclick="exportLogs()">Izvozi v Excel</button> <label>Rezultat</label>
<button class="btn btn-primary" onclick="printLogs()">Natisni</button>
<button id="addUnregisteredBtn" class="btn btn-primary" onclick="openAddUnregisteredModal()">Dodaj neregistriranega</button>
</div>
</div>
<div class="panel-filters">
<div class="filters-row">
<div class="filter-group">
<label>Rezultat:</label>
<select id="filterSelect" onchange="refreshLogs()"> <select id="filterSelect" onchange="refreshLogs()">
<option value="All">Vse</option> <option value="All">Vse</option>
<option value="PASS">Samo PASS</option> <option value="PASS">PASS</option>
<option value="FAIL">Samo FAIL</option> <option value="FAIL">FAIL</option>
</select> </select>
</div> </div>
<div class="filter-group"> <div class="tf-group">
<label>Udeleženec:</label> <label>Udeleženec</label>
<select id="participantFilter" onchange="refreshLogs()"> <select id="participantFilter" onchange="refreshLogs()">
<option value="">Vsi udeleženci</option> <option value="">Vsi</option>
</select> </select>
</div> </div>
<div class="filter-group"> <div class="tf-group">
<label>Od:</label> <label>Od</label>
<input type="date" id="startDateFilter" onchange="refreshLogs()"> <input type="date" id="startDateFilter" onchange="refreshLogs()">
</div> </div>
<div class="filter-group"> <div class="tf-group">
<label>Do:</label> <label>Do</label>
<input type="date" id="endDateFilter" onchange="refreshLogs()"> <input type="date" id="endDateFilter" onchange="refreshLogs()">
</div> </div>
<div class="tf-quick">
<button class="btn-quick-filter" onclick="filterByToday()">Danes</button>
<button class="btn-quick-filter" onclick="filterByLastWeek()">Teden</button>
<button class="btn-quick-filter" onclick="filterByLastMonth()">Mesec</button>
<button class="btn-quick-filter" onclick="filterByLastYear()">Leto</button>
<button class="btn-filter" onclick="resetFilters()">×</button>
</div>
</div> </div>
<div class="filters-quick"> <div class="toolbar-actions">
<button class="btn-quick-filter" onclick="filterByToday()">Danes</button> <button class="tb-btn" onclick="printLogs()">Natisni</button>
<button class="btn-quick-filter" onclick="filterByLastWeek()">Teden</button> <button id="addUnregisteredBtn" class="tb-btn tb-btn-primary" onclick="openAddUnregisteredModal()">+ Neregistriran</button>
<button class="btn-quick-filter" onclick="filterByLastMonth()">Mesec</button>
<button class="btn-quick-filter" onclick="filterByLastYear()">Leto</button>
<button class="btn-filter" onclick="resetFilters()">Resetiraj</button>
</div> </div>
</div> </div>
<div class="panel-content"> <div class="panel-content">
<div class="table-container"> <div class="table-container">
<table> <table>
@@ -145,8 +140,7 @@
<th id="logs-actions-header">Dejanja</th> <th id="logs-actions-header">Dejanja</th>
</tr> </tr>
</thead> </thead>
<tbody id="logsTable"> <tbody id="logsTable"></tbody>
</tbody>
</table> </table>
</div> </div>
</div> </div>
@@ -154,8 +148,8 @@
<!-- Documents Tab --> <!-- Documents Tab -->
<div id="documents-tab" class="tab-panel"> <div id="documents-tab" class="tab-panel">
<div class="panel-header"> <div class="toolbar">
<h2>Dokumenti</h2> <span class="toolbar-title">Dokumenti</span>
</div> </div>
<div class="panel-content"> <div class="panel-content">
<div id="documentsGrid" class="documents-grid"> <div id="documentsGrid" class="documents-grid">
@@ -166,82 +160,77 @@
<!-- Store Tab --> <!-- Store Tab -->
<div id="store-tab" class="tab-panel"> <div id="store-tab" class="tab-panel">
<div class="panel-header"> <!-- Single shared toolbar -->
<h2>Trgovina</h2> <div class="toolbar">
<div class="header-actions"> <span class="toolbar-title">Trgovina</span>
<div class="store-balance-display"> <div class="toolbar-actions">
<span class="store-balance-label">Stanje:</span> <div class="store-balance-chip">
<span id="storeBalanceDisplay" class="store-balance-value">€0.00</span> <span class="store-balance-label">Stanje</span>
<button id="editBalanceBtn" class="btn btn-primary" onclick="openEditBalanceModal()" style="display:none;">Uredi stanje</button> <span id="storeBalanceDisplay" class="store-balance-value">€0.00</span>
</div> </div>
<button class="btn btn-primary" onclick="togglePaymentHistory()">Zgodovina plačil</button> <button id="editBalanceBtn" class="tb-btn" onclick="openEditBalanceModal()" style="display:none;">Uredi stanje</button>
</div> <button id="addItemBtn" class="tb-btn tb-btn-primary" onclick="openAddItemModal()" style="display:none;">+ Artikel</button>
</div> <button id="storeViewToggleBtn" class="tb-btn" onclick="toggleStoreView()">Zgodovina plačil</button>
<div class="store-tab-scroll panel-content">
<div class="store-layout">
<!-- Inventory -->
<div class="store-inventory">
<div class="store-section-header">
<h3>Zaloga</h3>
<button id="addItemBtn" class="btn btn-primary" onclick="openAddItemModal()" style="display:none;">+ Dodaj artikel</button>
</div>
<div class="inventory-grid" id="inventoryGrid">
<p style="color: var(--text-secondary);">Nalaganje...</p>
</div>
</div>
<!-- Cart -->
<div class="store-cart">
<div class="store-section-header">
<h3>Košarica</h3>
<button class="btn btn-primary" onclick="clearCart()">Počisti</button>
</div>
<div class="cart-items" id="cartItems">
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">Košarica je prazna</p>
</div>
<div class="cart-total">
<div class="cart-total-row">
<span>Skupaj:</span>
<span id="cartTotal" class="cart-total-amount">€0.00</span>
</div> </div>
<div class="cart-total-row cart-balance-row"> </div>
<span>Stanje po plačilu:</span>
<span id="cartBalanceAfter" class="cart-balance-after">€0.00</span> <!-- Shop view -->
<div id="storeShopView" class="store-tab-scroll panel-content">
<div class="store-layout">
<div class="store-inventory">
<div class="inventory-grid" id="inventoryGrid">
<p style="color: var(--text-secondary);">Nalaganje...</p>
</div>
</div>
<div class="store-cart">
<div class="store-cart-header">
<span class="store-cart-title">Košarica</span>
<button class="tb-btn" onclick="clearCart()">Počisti</button>
</div>
<div class="cart-items" id="cartItems">
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">Košarica je prazna</p>
</div>
<div class="cart-total">
<div class="cart-total-row">
<span>Skupaj:</span>
<span id="cartTotal" class="cart-total-amount">€0.00</span>
</div>
<div class="cart-total-row cart-balance-row">
<span>Stanje po plačilu:</span>
<span id="cartBalanceAfter" class="cart-balance-after">€0.00</span>
</div>
<button class="btn btn-primary cart-pay-btn" onclick="processPayment()">Plačaj</button>
</div>
</div>
</div>
</div>
<!-- History view -->
<div id="storeHistoryView" class="panel-content" style="display:none;">
<div class="history-toolbar">
<button class="tb-btn" onclick="openMonthlyOverview()">Mesečni pregled</button>
<button id="clearHistoryBtn" class="tb-btn" onclick="clearPaymentHistory()" style="display:none;color:var(--accent-red);border-color:#fca5a5;">Izbriši zgodovino</button>
</div>
<div class="table-container" style="height:calc(100% - 46px);">
<table>
<thead>
<tr>
<th>#</th>
<th>Prodajalec</th>
<th>Datum</th>
<th>Čas</th>
<th>Artikli</th>
<th>Skupaj</th>
<th>Stanje pred</th>
<th>Stanje po</th>
<th>Potrdilo</th>
</tr>
</thead>
<tbody id="paymentHistoryTable"></tbody>
</table>
</div> </div>
<button class="btn btn-primary cart-pay-btn" onclick="processPayment()">Plačaj</button>
</div> </div>
</div> </div>
</div>
</div><!-- end store-layout -->
<!-- Payment History Panel -->
<div id="paymentHistoryPanel" class="payment-history-panel" style="display:none;">
<div class="store-section-header" style="padding: 0 16px 12px;">
<h3>Zgodovina plačil</h3>
<button id="clearHistoryBtn" class="action-btn delete-btn" onclick="clearPaymentHistory()" style="display:none;">Izbriši zgodovino</button>
</div>
<div class="table-container" style="margin: 0 16px 16px;">
<table>
<thead>
<tr>
<th>#</th>
<th>Datum</th>
<th>Čas</th>
<th>Artikli</th>
<th>Skupaj</th>
<th>Stanje pred</th>
<th>Stanje po</th>
</tr>
</thead>
<tbody id="paymentHistoryTable">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Add Item Modal --> <!-- Add Item Modal -->
<div id="addItemModal" class="modal"> <div id="addItemModal" class="modal">
@@ -367,6 +356,54 @@
</div> </div>
</div> </div>
<!-- Checkout Modal -->
<div id="checkoutModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeCheckoutModal()">&times;</span>
<h2>Podatki prodajalca</h2>
<div class="form-group">
<label>Ime:</label>
<input type="text" id="checkoutFirstName" placeholder="Ime">
</div>
<div class="form-group">
<label>Priimek:</label>
<input type="text" id="checkoutLastName" placeholder="Priimek" onkeydown="if(event.key==='Enter')confirmCheckout()">
</div>
<div class="checkout-summary">
<div class="checkout-summary-row">
<span>Skupaj za plačilo:</span>
<span id="checkoutTotal" class="checkout-total-val"></span>
</div>
<div class="checkout-summary-row">
<span>Stanje po plačilu:</span>
<span id="checkoutBalanceAfter" class="checkout-balance-val"></span>
</div>
</div>
<button class="btn btn-primary" onclick="confirmCheckout()">Potrdi plačilo</button>
</div>
</div>
<!-- Monthly Overview Modal -->
<div id="monthlyModal" class="modal">
<div class="modal-content modal-content-wide">
<div class="monthly-modal-header">
<h2>Mesečni pregled</h2>
<span class="close" onclick="closeMonthlyOverview()">&times;</span>
</div>
<div class="monthly-modal-toolbar">
<div class="filter-group" style="flex:0 0 auto;min-width:180px;">
<label>Mesec:</label>
<input type="month" id="monthlyPickerInput" onchange="renderMonthlyOverview()">
</div>
<button class="btn btn-primary" onclick="printMonthlyOverview()" style="width:auto;padding:10px 18px;margin:0;font-size:12px;flex-shrink:0;">Natisni pregled</button>
</div>
<div id="monthlyOverviewContent" class="monthly-modal-body"></div>
</div>
</div>
<!-- Hidden receipt for printing -->
<div id="receiptPrintArea" style="display:none;"></div>
<div id="print-header" style="visibility:hidden;position:fixed;"> <div id="print-header" style="visibility:hidden;position:fixed;">
<img src="{{ url_for('static', filename='assets/logo.png') }}" alt="Logo"> <img src="{{ url_for('static', filename='assets/logo.png') }}" alt="Logo">
<p id="print-date-range" style="font-size:13px;color:#444;margin-top:4px;"></p> <p id="print-date-range" style="font-size:13px;color:#444;margin-top:4px;"></p>