let editingParticipantId = null; let isAdmin = false; // Initialize on page load document.addEventListener('DOMContentLoaded', function() { checkAdminStatus(); loadParticipants(); // Show today's entries by default filterByToday(); updateLastScanResult(); // Auto-refresh logs every 5 seconds setInterval(refreshLogs, 5000); setInterval(loadParticipants, 5000); setInterval(updateLastScanResult, 1000); setInterval(checkAdminStatus, 10000); }); // ===== AUTHENTICATION ===== async function checkAdminStatus() { try { const response = await fetch('/api/check-admin'); const data = await response.json(); isAdmin = data.is_admin; updateAdminUI(); } catch (error) { console.error('Error checking admin status:', error); } } function updateAdminUI() { const adminBtn = document.getElementById('adminToggleBtn'); const addBtn = document.querySelector('[onclick="openAddParticipantModal()"]'); const addUnregisteredBtn = document.getElementById('addUnregisteredBtn'); const addItemBtn = document.getElementById('addItemBtn'); const editBalanceBtn = document.getElementById('editBalanceBtn'); if (!adminBtn) return; if (isAdmin) { adminBtn.textContent = 'Odjava Admin'; adminBtn.onclick = adminLogout; adminBtn.classList.add('active'); if (addBtn) addBtn.style.display = 'block'; if (addItemBtn) addItemBtn.style.display = 'inline-flex'; if (editBalanceBtn) editBalanceBtn.style.display = 'inline-flex'; } else { adminBtn.textContent = 'Prijava Admin'; adminBtn.onclick = openLoginModal; adminBtn.classList.remove('active'); if (addBtn) addBtn.style.display = 'none'; if (addItemBtn) addItemBtn.style.display = 'none'; if (editBalanceBtn) editBalanceBtn.style.display = 'none'; } if (addUnregisteredBtn) addUnregisteredBtn.style.display = 'block'; updateUIDVisibility(); } async function adminLogin() { const password = document.getElementById('loginPassword').value; if (!password) { alert('Prosimo, vnesite geslo'); return; } try { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) }); const data = await response.json(); if (data.success) { isAdmin = true; closeLoginModal(); updateAdminUI(); loadParticipants(); loadStore(); alert('Uspešna prijava!'); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } async function adminLogout() { try { const response = await fetch('/api/logout', { method: 'POST' }); if (response.ok) { isAdmin = false; updateAdminUI(); loadParticipants(); loadStore(); alert('Odjavljen'); } } catch (error) { console.error('Logout error:', error); } } function openLoginModal() { document.getElementById('loginModal').classList.add('show'); document.getElementById('loginPassword').focus(); } function closeLoginModal() { document.getElementById('loginModal').classList.remove('show'); document.getElementById('loginPassword').value = ''; } function handleLoginKeypress(event) { if (event.key === 'Enter') { adminLogin(); } } // ===== PARTICIPANT MANAGEMENT ===== async function loadParticipants() { try { const response = await fetch('/api/participants'); const participants = await response.json(); const tbody = document.getElementById('participantsTable'); tbody.innerHTML = ''; participants.forEach(p => { const row = document.createElement('tr'); const dateAdded = p.date_added ? p.date_added.split('T')[0] : '-'; const expiration = p.expiration_date ? p.expiration_date.split('T')[0] : '-'; // Check if card is expired let isExpired = false; if (expiration !== '-') { const expirationDate = new Date(expiration); const today = new Date(); today.setHours(0, 0, 0, 0); isExpired = expirationDate < today; } const keyUID = p.key_uid ? p.key_uid : '-'; const actionButtons = isAdmin ? ` ` : ''; row.innerHTML = ` ${p.id} ${p.name} ${p.card_uid || '-'} ${keyUID} ${expiration} ${dateAdded} ${actionButtons || '-'} `; // Add expired class if card is expired if (isExpired) { row.classList.add('expired'); } tbody.appendChild(row); }); // Update participant filter dropdown updateParticipantFilter(participants); updateUIDVisibility(); } catch (error) { console.error('Error loading participants:', error); } } function updateParticipantFilter(participants) { const filterSelect = document.getElementById('participantFilter'); const currentValue = filterSelect.value; // Clear existing options except the first one filterSelect.innerHTML = ''; // Sort participants by name and add to dropdown participants .sort((a, b) => a.name.localeCompare(b.name)) .forEach(p => { const option = document.createElement('option'); option.value = p.name; option.textContent = p.name; filterSelect.appendChild(option); }); // Restore previous selection if it still exists filterSelect.value = currentValue; } async function addParticipant() { const name = document.getElementById('inputName').value.trim(); const cardUID = document.getElementById('inputCardUID').value.trim(); const keyUID = document.getElementById('inputKeyUID').value.trim(); const expiration = document.getElementById('inputExpiration').value; if (!name) { alert('Prosimo, izpolnite polje za ime'); return; } if (!cardUID && !keyUID) { alert('Prosimo, vnesite vsaj UID kartice ali UID ključa'); return; } try { const response = await fetch('/api/participants', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name, card_uid: cardUID || null, key_uid: keyUID || null, expiration_date: expiration || null }) }); if (!response.ok) { const errorData = await response.json(); alert('Napaka: ' + (errorData.message || 'Unknown error')); return; } const data = await response.json(); if (data.success) { alert('Udeleženec je bil uspešno dodan'); closeAddParticipantModal(); loadParticipants(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } function openEditModal(id, name, cardUID, keyUID, expiration) { editingParticipantId = id; document.getElementById('editName').value = name; document.getElementById('editCardUID').value = cardUID; document.getElementById('editKeyUID').value = keyUID; document.getElementById('editExpiration').value = expiration !== '-' ? expiration : ''; document.getElementById('editModal').classList.add('show'); } function openAddParticipantModal() { document.getElementById('addParticipantModal').classList.add('show'); } function closeAddParticipantModal() { document.getElementById('addParticipantModal').classList.remove('show'); document.getElementById('inputName').value = ''; document.getElementById('inputCardUID').value = ''; document.getElementById('inputKeyUID').value = ''; document.getElementById('inputExpiration').value = ''; } function openAddUnregisteredModal() { document.getElementById('addUnregisteredModal').classList.add('show'); document.getElementById('inputUnregisteredName').focus(); } function closeAddUnregisteredModal() { document.getElementById('addUnregisteredModal').classList.remove('show'); document.getElementById('inputUnregisteredName').value = ''; } async function addUnregisteredUser() { const name = document.getElementById('inputUnregisteredName').value.trim(); if (!name) { alert('Prosimo, izpolnite polje za ime'); return; } try { const response = await fetch('/api/logs/unregistered', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name }) }); const data = await response.json(); if (data.success) { alert('Neregistrirani udeleženec je bil uspešno dodan'); closeAddUnregisteredModal(); refreshLogs(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } function closeModal() { document.getElementById('editModal').classList.remove('show'); editingParticipantId = null; } async function saveChanges() { if (!editingParticipantId) return; const name = document.getElementById('editName').value.trim(); const cardUID = document.getElementById('editCardUID').value.trim(); const keyUID = document.getElementById('editKeyUID').value.trim(); const expiration = document.getElementById('editExpiration').value; if (!name) { alert('Prosimo, izpolnite polje za ime'); return; } if (!cardUID && !keyUID) { alert('Prosimo, vnesite vsaj UID kartice ali UID ključa'); return; } try { const response = await fetch(`/api/participants/${editingParticipantId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name, card_uid: cardUID, key_uid: keyUID, expiration_date: expiration || null }) }); if (!response.ok) { const errorData = await response.json(); alert('Napaka: ' + (errorData.message || 'Unknown error')); return; } const data = await response.json(); if (data.success) { alert('Udeleženec je bil uspešno posodobljen'); closeModal(); loadParticipants(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } async function deleteParticipant(id) { if (!confirm('Ali ste prepričani, da želite izbrisati tega udeleženca?')) return; try { const response = await fetch(`/api/participants/${id}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { alert('Udeleženec je bil uspešno izbrisan'); loadParticipants(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } // ===== LOGS MANAGEMENT ===== async function refreshLogs() { try { const filter = document.getElementById('filterSelect').value || 'All'; const response = await fetch(`/api/logs?filter=${filter}`); const logs = await response.json(); // Apply client-side filtering const filteredLogs = applyLogFilters(logs); const tbody = document.getElementById('logsTable'); tbody.innerHTML = ''; if (filteredLogs.length === 0) { tbody.innerHTML = `Ni najdenih dnevnikov, ki bi ustrezali filtrom`; return; } // Count occurrences of each name to detect duplicates const nameCounts = {}; filteredLogs.forEach(log => { nameCounts[log.name] = (nameCounts[log.name] || 0) + 1; }); // Count registered vs unregistered users let registeredCount = 0; let unregisteredCount = 0; filteredLogs.forEach(log => { const row = document.createElement('tr'); const timestamp = log.timestamp; const date = timestamp.split('T')[0]; const time = timestamp.split('T')[1].substring(0, 8); // Determine status class let statusClass = 'pass'; if (log.scan_result === 'FAIL') { statusClass = 'fail'; } else if (log.scan_result === 'MANUAL') { statusClass = 'manual'; } // Check if this is an unregistered duplicate const isDuplicate = nameCounts[log.name] > 1; const actionButtons = isAdmin ? ` ` : ''; row.innerHTML = ` ${log.id} ${log.name} ${log.card_uid || '-'} ${log.key_uid || '-'} ${log.scan_result} ${date} ${time} ${actionButtons} `; // Add duplicate warning class if this is a duplicate unregistered user if (isDuplicate && log.scan_result === 'MANUAL') { row.classList.add('duplicate-warning'); } // Count registered vs unregistered if (log.scan_result === 'MANUAL') { unregisteredCount++; } else if (log.scan_result === 'PASS') { registeredCount++; } tbody.appendChild(row); }); // Update stats display document.getElementById('statRegistered').textContent = registeredCount; document.getElementById('statUnregistered').textContent = unregisteredCount; updateUIDVisibility(); } catch (error) { console.error('Error loading logs:', error); } } function applyLogFilters(logs) { const participantFilter = document.getElementById('participantFilter').value; const startDateFilter = document.getElementById('startDateFilter').value; const endDateFilter = document.getElementById('endDateFilter').value; return logs.filter(log => { // Filter by participant if (participantFilter && log.name !== participantFilter) { return false; } // Filter by date range if (startDateFilter || endDateFilter) { const logDate = log.timestamp.split('T')[0]; if (startDateFilter && logDate < startDateFilter) { return false; } if (endDateFilter && logDate > endDateFilter) { return false; } } return true; }); } async function deleteLog(logId) { if (!confirm('Ali ste prepričani, da želite izbrisati ta dnevnik?')) return; try { const response = await fetch(`/api/logs/${logId}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { alert('Dnevnik je bil uspešno izbrisan'); refreshLogs(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } function resetFilters() { document.getElementById('filterSelect').value = 'All'; document.getElementById('participantFilter').value = ''; document.getElementById('startDateFilter').value = ''; document.getElementById('endDateFilter').value = ''; // Remove active class from all quick filter buttons document.querySelectorAll('.btn-quick-filter').forEach(btn => { btn.classList.remove('active'); }); refreshLogs(); } function getDateString(date) { return date.toISOString().split('T')[0]; } function filterByToday() { const today = new Date(); document.getElementById('startDateFilter').value = getDateString(today); document.getElementById('endDateFilter').value = getDateString(today); updateQuickFilterButtons('today'); refreshLogs(); } function filterByLastWeek() { const today = new Date(); const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); document.getElementById('startDateFilter').value = getDateString(lastWeek); document.getElementById('endDateFilter').value = getDateString(today); updateQuickFilterButtons('week'); refreshLogs(); } function filterByLastMonth() { const today = new Date(); const lastMonth = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000); document.getElementById('startDateFilter').value = getDateString(lastMonth); document.getElementById('endDateFilter').value = getDateString(today); updateQuickFilterButtons('month'); refreshLogs(); } function filterByLastYear() { const today = new Date(); const lastYear = new Date(today.getTime() - 365 * 24 * 60 * 60 * 1000); document.getElementById('startDateFilter').value = getDateString(lastYear); document.getElementById('endDateFilter').value = getDateString(today); updateQuickFilterButtons('year'); refreshLogs(); } function updateQuickFilterButtons(activeFilter) { const buttons = document.querySelectorAll('.btn-quick-filter'); buttons.forEach(btn => btn.classList.remove('active')); if (activeFilter === 'today') buttons[0].classList.add('active'); else if (activeFilter === 'week') buttons[1].classList.add('active'); else if (activeFilter === 'month') buttons[2].classList.add('active'); else if (activeFilter === 'year') buttons[3].classList.add('active'); } async function exportLogs() { try { // Get current filter values const filter = document.getElementById('filterSelect').value; const participant = document.getElementById('participantFilter').value; const startDate = document.getElementById('startDateFilter').value; const endDate = document.getElementById('endDateFilter').value; // Build query string with filters const params = new URLSearchParams({ filter: filter, participant: participant, startDate: startDate, endDate: endDate }); const response = await fetch('/api/export?' + params.toString()); const result = await response.json(); if (!result.success) { alert('Napaka pri izvozу: ' + result.message); return; } // Decode base64 file data const binaryString = atob(result.data); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } // Create blob and trigger download with file dialog const blob = new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); // Try to use pywebview if available (for desktop app) if (window.pywebview && window.pywebview.api) { try { const success = await window.pywebview.api.save_file(result.filename, result.data); if (success) { alert('Datoteka je bila uspešno izvožena'); return; } else { console.log('PyWebView save was cancelled, falling back to browser download'); } } catch (error) { console.log('PyWebView save failed, falling back to browser download', error); } } // Fallback to browser download const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = result.filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); alert('Datoteka je bila uspešno izvožena'); } catch (error) { alert('Napaka: ' + error.message); } } // ===== PRINT LOGS ===== function printLogs() { const start = document.getElementById('startDateFilter').value; const end = document.getElementById('endDateFilter').value; const dateRange = document.getElementById('print-date-range'); if (start && end) { dateRange.textContent = `Obdobje: ${start} – ${end}`; } else if (start) { dateRange.textContent = `Od: ${start}`; } else if (end) { dateRange.textContent = `Do: ${end}`; } else { dateRange.textContent = `Natisnjeno: ${new Date().toLocaleDateString('sl-SI')}`; } window.print(); } // ===== DOCUMENTS ===== async function loadDocuments() { try { const response = await fetch('/api/documents'); const files = await response.json(); const grid = document.getElementById('documentsGrid'); if (files.length === 0) { grid.innerHTML = `

Ni dokumentov. Dodajte PDF datoteke v mapo static/PDF.

`; return; } grid.innerHTML = ''; files.forEach(filename => { const card = document.createElement('div'); card.className = 'document-card'; const displayName = filename.replace(/\.pdf$/i, '').replace(/_/g, ' '); card.innerHTML = `
${displayName}
`; grid.appendChild(card); }); } catch (error) { console.error('Error loading documents:', error); } } function printDocument(filename) { const url = `/static/PDF/${encodeURIComponent(filename)}`; let iframe = document.getElementById('print-iframe'); if (!iframe) { iframe = document.createElement('iframe'); iframe.id = 'print-iframe'; iframe.style.cssText = 'display:none;position:fixed;top:0;left:0;width:0;height:0;'; document.body.appendChild(iframe); } iframe.onload = () => iframe.contentWindow.print(); iframe.src = url; } // ===== TAB SWITCHING ===== function switchTab(tabName, event) { // Hide all tabs document.querySelectorAll('.tab-panel').forEach(tab => { tab.classList.remove('active'); }); // Remove active class from all buttons document.querySelectorAll('.tab-nav-btn').forEach(btn => { btn.classList.remove('active'); }); // Show selected tab document.getElementById(tabName + '-tab').classList.add('active'); // Add active class to clicked button event.target.classList.add('active'); // Load tab-specific data if (tabName === 'documents') loadDocuments(); if (tabName === 'store') loadStore(); } // Close modal when clicking outside window.onclick = function(event) { const editModal = document.getElementById('editModal'); const addModal = document.getElementById('addParticipantModal'); const loginModal = document.getElementById('loginModal'); const addUnregisteredModal = document.getElementById('addUnregisteredModal'); const addItemModal = document.getElementById('addItemModal'); const editBalanceModal = document.getElementById('editBalanceModal'); if (event.target === editModal) closeModal(); if (event.target === addModal) closeAddParticipantModal(); if (event.target === loginModal) closeLoginModal(); if (event.target === addUnregisteredModal) closeAddUnregisteredModal(); if (event.target === addItemModal) closeAddItemModal(); if (event.target === editBalanceModal) closeEditBalanceModal(); } // ===== LAST SCAN RESULT ===== let lastScannedUID = '-'; let lastScanTimestamp = null; async function updateLastScanResult() { try { const response = await fetch('/api/last-scan'); const result = await response.json(); const resultCard = document.getElementById('resultCard'); const uidDiv = resultCard.querySelector('.result-uid'); const nameDiv = resultCard.querySelector('.result-name'); const statusDiv = resultCard.querySelector('.result-status'); uidDiv.textContent = result.uid; nameDiv.textContent = result.name; statusDiv.textContent = result.status; lastScannedUID = result.uid; // If a new scan just happened, immediately refresh the logs table if (result.timestamp && result.timestamp !== lastScanTimestamp) { lastScanTimestamp = result.timestamp; refreshLogs(); } // Update card styling based on result resultCard.className = 'result-card'; if (result.status === 'PASS') { resultCard.classList.add('pass'); } else if (result.status === 'FAIL') { resultCard.classList.add('fail'); } } catch (error) { console.error('Error updating last scan result:', error); } } function copyUID(evt) { if (lastScannedUID === '-') { alert('Ni skeniranega UID-ja za kopiranje'); return; } const copyBtn = evt.currentTarget; // Try using clipboard API first if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(lastScannedUID).then(() => { showCopySuccess(copyBtn); }).catch(err => { console.error('Clipboard API failed:', err); fallbackCopy(lastScannedUID, copyBtn); }); } else { // Fallback for older browsers fallbackCopy(lastScannedUID, copyBtn); } } function fallbackCopy(text, copyBtn) { const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); showCopySuccess(copyBtn); } catch (err) { console.error('Fallback copy failed:', err); alert('Napaka pri kopiranju UID-ja'); } document.body.removeChild(textarea); } function showCopySuccess(copyBtn) { copyBtn.style.color = 'var(--accent-green)'; setTimeout(() => { copyBtn.style.color = ''; }, 1500); } // ===== UID VISIBILITY MANAGEMENT ===== function updateUIDVisibility() { const display = isAdmin ? '' : 'none'; // Participants table columns const cardUIDHeader = document.getElementById('card-uid-header'); const keyUIDHeader = document.getElementById('key-uid-header'); if (cardUIDHeader) cardUIDHeader.style.display = display; if (keyUIDHeader) keyUIDHeader.style.display = display; // Logs table columns const logsCardUIDHeader = document.getElementById('logs-card-uid-header'); const logsKeyUIDHeader = document.getElementById('logs-key-uid-header'); const logsActionsHeader = document.getElementById('logs-actions-header'); if (logsCardUIDHeader) logsCardUIDHeader.style.display = display; if (logsKeyUIDHeader) logsKeyUIDHeader.style.display = display; if (logsActionsHeader) logsActionsHeader.style.display = display; // Hide/show corresponding cells in all table rows hideTableColumn('participantsTable', 2, !isAdmin); // Card UID column hideTableColumn('participantsTable', 3, !isAdmin); // Key UID column hideTableColumn('logsTable', 2, !isAdmin); // Card UID column hideTableColumn('logsTable', 3, !isAdmin); // Key UID column hideTableColumn('logsTable', 7, !isAdmin); // Actions column // Last scan result UID const scanUIDContainer = document.getElementById('scan-result-uid-container'); if (scanUIDContainer) scanUIDContainer.style.display = display; } // ===== STORE ===== let cart = []; let storeItems = []; let currentBalance = 0; let editingItemId = null; async function loadStore() { await Promise.all([loadStoreItems(), loadBalance()]); } async function loadStoreItems() { try { const response = await fetch('/api/store/items'); storeItems = await response.json(); renderInventory(); renderCart(); } catch (error) { console.error('Error loading store items:', error); } } async function loadBalance() { try { const response = await fetch('/api/store/balance'); const data = await response.json(); currentBalance = data.balance; document.getElementById('storeBalanceDisplay').textContent = '€' + currentBalance.toFixed(2); updateCartTotals(); } catch (error) { console.error('Error loading balance:', error); } } const ITEM_FALLBACK_SVG = ``; function renderInventory() { const grid = document.getElementById('inventoryGrid'); if (!grid) return; if (storeItems.length === 0) { grid.innerHTML = '

Ni artiklov v zalogi.

'; return; } grid.innerHTML = ''; storeItems.forEach(item => { const card = document.createElement('div'); card.className = 'inventory-card' + (item.stock === 0 ? ' out-of-stock' : ''); const imgHtml = item.image ? `${item.name}` : `
${ITEM_FALLBACK_SVG}
`; card.innerHTML = ` ${imgHtml}
${item.name}
€${item.price.toFixed(2)}
Zaloga: ${item.stock}
${isAdmin ? ` ` : ''}
`; grid.appendChild(card); }); } function addToCart(itemId) { const item = storeItems.find(i => i.id === itemId); if (!item || item.stock === 0) return; const existing = cart.find(c => c.id === itemId); const currentQty = existing ? existing.qty : 0; if (currentQty >= item.stock) { alert('Ni dovolj zaloge'); return; } if (existing) { existing.qty++; } else { cart.push({ id: item.id, name: item.name, price: item.price, qty: 1 }); } renderCart(); } function removeFromCart(itemId) { const idx = cart.findIndex(c => c.id === itemId); if (idx === -1) return; if (cart[idx].qty > 1) { cart[idx].qty--; } else { cart.splice(idx, 1); } renderCart(); } function clearCart() { cart = []; renderCart(); } function renderCart() { const container = document.getElementById('cartItems'); if (!container) return; if (cart.length === 0) { container.innerHTML = '

Košarica je prazna

'; updateCartTotals(); return; } container.innerHTML = ''; cart.forEach(item => { const row = document.createElement('div'); row.className = 'cart-item'; row.innerHTML = `
${item.name}
${item.qty}
€${(item.price * item.qty).toFixed(2)}
`; container.appendChild(row); }); updateCartTotals(); } function updateCartTotals() { const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0); const balanceAfter = currentBalance + total; const totalEl = document.getElementById('cartTotal'); const balanceAfterEl = document.getElementById('cartBalanceAfter'); if (totalEl) totalEl.textContent = '€' + total.toFixed(2); if (balanceAfterEl) { balanceAfterEl.textContent = '€' + balanceAfter.toFixed(2); balanceAfterEl.className = 'cart-balance-after'; } } async function processPayment() { if (cart.length === 0) { alert('Košarica je prazna'); return; } const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0); const balanceAfter = currentBalance + total; if (!confirm(`Skupaj: €${total.toFixed(2)}\nStanje po plačilu: €${balanceAfter.toFixed(2)}\n\nPotrdite plačilo?`)) return; try { const cartItems = cart.map(cartItem => { const storeItem = storeItems.find(i => i.id === cartItem.id); return { id: cartItem.id, name: cartItem.name, price: cartItem.price, qty: cartItem.qty, new_stock: storeItem ? storeItem.stock - cartItem.qty : 0, image: storeItem ? storeItem.image : null }; }); const response = await fetch('/api/store/payments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })), cart_items: cartItems, total, balance_before: currentBalance, balance_after: balanceAfter }) }); const data = await response.json(); if (!data.success) { alert('Napaka: ' + data.message); return; } cart = []; await loadStore(); alert('Plačilo uspešno!'); } catch (error) { alert('Napaka: ' + error.message); } } let currentItemImage = null; function openAddItemModal() { editingItemId = null; currentItemImage = null; document.getElementById('addItemModalTitle').textContent = 'Dodaj artikel'; document.getElementById('addItemSubmitBtn').textContent = 'Dodaj'; document.getElementById('itemName').value = ''; document.getElementById('itemPrice').value = ''; document.getElementById('itemStock').value = ''; document.getElementById('itemImageInput').value = ''; setImagePreview(null); document.getElementById('addItemModal').classList.add('show'); document.getElementById('itemName').focus(); } function openEditItemModal(itemId) { const item = storeItems.find(i => i.id === itemId); if (!item) return; editingItemId = itemId; currentItemImage = item.image || null; document.getElementById('addItemModalTitle').textContent = 'Uredi artikel'; document.getElementById('addItemSubmitBtn').textContent = 'Shrani'; document.getElementById('itemName').value = item.name; document.getElementById('itemPrice').value = item.price; document.getElementById('itemStock').value = item.stock; document.getElementById('itemImageInput').value = ''; setImagePreview(item.image || null); document.getElementById('addItemModal').classList.add('show'); } function closeAddItemModal() { document.getElementById('addItemModal').classList.remove('show'); editingItemId = null; currentItemImage = null; } function handleImageUpload(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { currentItemImage = e.target.result; setImagePreview(currentItemImage); }; reader.readAsDataURL(file); } function setImagePreview(src) { const preview = document.getElementById('imagePreview'); const placeholder = document.getElementById('imageUploadPlaceholder'); const removeBtn = document.getElementById('removeImageBtn'); if (src) { preview.src = src; preview.style.display = 'block'; placeholder.style.display = 'none'; removeBtn.style.display = 'inline-block'; } else { preview.src = ''; preview.style.display = 'none'; placeholder.style.display = 'flex'; removeBtn.style.display = 'none'; } } function removeItemImage() { currentItemImage = null; document.getElementById('itemImageInput').value = ''; setImagePreview(null); } async function submitItem() { const name = document.getElementById('itemName').value.trim(); const price = parseFloat(document.getElementById('itemPrice').value); const stock = parseInt(document.getElementById('itemStock').value, 10); if (!name) { alert('Vnesite ime artikla'); return; } if (isNaN(price) || price < 0) { alert('Vnesite veljavno ceno'); return; } if (isNaN(stock) || stock < 0) { alert('Vnesite veljavno zalogo'); return; } try { let response; if (editingItemId) { response = await fetch(`/api/store/items/${editingItemId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, price, stock, image: currentItemImage }) }); } else { response = await fetch('/api/store/items', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, price, stock, image: currentItemImage }) }); } const data = await response.json(); if (data.success) { closeAddItemModal(); await loadStoreItems(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } async function deleteStoreItem(itemId) { if (!confirm('Izbriši artikel?')) return; try { const response = await fetch(`/api/store/items/${itemId}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { cart = cart.filter(c => c.id !== itemId); await loadStoreItems(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } function openEditBalanceModal() { document.getElementById('balanceInput').value = currentBalance.toFixed(2); document.getElementById('editBalanceModal').classList.add('show'); document.getElementById('balanceInput').focus(); } function closeEditBalanceModal() { document.getElementById('editBalanceModal').classList.remove('show'); } async function saveBalance() { const amount = parseFloat(document.getElementById('balanceInput').value); if (isNaN(amount)) { alert('Vnesite veljavno vrednost'); return; } try { const response = await fetch('/api/store/balance', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ balance: amount }) }); const data = await response.json(); if (data.success) { closeEditBalanceModal(); await loadBalance(); } else { alert('Napaka: ' + data.message); } } catch (error) { alert('Napaka: ' + error.message); } } // ===== PAYMENT HISTORY ===== let paymentHistoryVisible = false; function togglePaymentHistory() { paymentHistoryVisible = !paymentHistoryVisible; const panel = document.getElementById('paymentHistoryPanel'); panel.style.display = paymentHistoryVisible ? 'block' : 'none'; if (paymentHistoryVisible) loadPaymentHistory(); } async function loadPaymentHistory() { try { const response = await fetch('/api/store/payments'); const payments = await response.json(); const tbody = document.getElementById('paymentHistoryTable'); if (!tbody) return; const clearBtn = document.getElementById('clearHistoryBtn'); if (clearBtn) clearBtn.style.display = isAdmin ? 'inline-block' : 'none'; if (payments.length === 0) { tbody.innerHTML = `Ni zabeleženih plačil`; return; } tbody.innerHTML = ''; [...payments].reverse().forEach(p => { const ts = p.timestamp; const date = ts.split('T')[0]; const time = ts.split('T')[1].substring(0, 8); const itemsSummary = p.items.map(i => `${i.name} ×${i.qty}`).join(', '); const row = document.createElement('tr'); row.innerHTML = ` ${p.id} ${date} ${time} ${itemsSummary} €${p.total.toFixed(2)} €${p.balance_before.toFixed(2)} €${p.balance_after.toFixed(2)} `; tbody.appendChild(row); }); } catch (error) { console.error('Error loading payment history:', error); } } async function clearPaymentHistory() { if (!confirm('Izbriši celotno zgodovino plačil?')) return; try { const response = await fetch('/api/store/payments', { method: 'DELETE' }); const data = await response.json(); if (data.success) loadPaymentHistory(); else alert('Napaka: ' + data.message); } catch (error) { alert('Napaka: ' + error.message); } } function hideTableColumn(tableId, columnIndex, hide) { const table = document.getElementById(tableId); if (!table) return; const rows = table.getElementsByTagName('tr'); for (let row of rows) { const cell = row.cells[columnIndex]; if (cell) { cell.style.display = hide ? 'none' : ''; } } }