From d3e50305a757c0bd748da23ec9c90e7ed43f0d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Wed, 16 Jul 2025 09:04:01 +0200 Subject: [PATCH] poprawki w js --- static/css/style.css | 48 ++++-- static/js/clickable_row.js | 2 +- static/js/expenses.js | 84 +++++------ static/js/functions.js | 95 ------------ static/js/live.js | 42 +++--- static/js/notes.js | 1 + static/js/product_suggestion.js | 60 ++++---- static/js/receipt_section.js | 2 +- static/js/sockets.js | 88 +++++------ static/js/toggle_button.js | 4 +- static/js/user_management.js | 2 +- templates/admin/admin_panel.html | 218 ++++++++++++++------------- templates/admin/edit_list.html | 185 ++++++++++++----------- templates/admin/list_products.html | 33 ++-- templates/admin/receipts.html | 48 +++--- templates/admin/user_management.html | 25 ++- templates/base.html | 115 +++++++------- templates/edit_my_list.html | 2 +- templates/errors.html | 2 +- templates/list.html | 165 ++++++++++---------- templates/list_share.html | 171 +++++++++++---------- templates/login.html | 6 +- templates/main.html | 189 +++++++++++------------ templates/system_auth.html | 9 +- 24 files changed, 779 insertions(+), 817 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index 878f7e0..0e32abb 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -3,6 +3,7 @@ width: 1.5em; height: 1.5em; } + .clickable-item { cursor: pointer; } @@ -38,7 +39,8 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - pointer-events: none; /* klikalne przyciski obok paska nie ucierpią */ + pointer-events: none; + /* klikalne przyciski obok paska nie ucierpią */ white-space: nowrap; } @@ -53,7 +55,7 @@ /* --- Styl przycisku wyboru pliku --- */ input[type="file"]::file-selector-button { - background-color: #225d36; + background-color: #225d36; color: #fff; border: none; padding: 0.5em 1em; @@ -69,16 +71,19 @@ input[type="file"]::file-selector-button { color: #eaffea !important; border-color: #174428 !important; } + .alert-danger { background-color: #7a1f23 !important; color: #ffeaea !important; border-color: #531417 !important; } + .alert-info { background-color: #1d3a4d !important; color: #eaf6ff !important; border-color: #152837 !important; } + .alert-warning { background-color: #665c1e !important; color: #fffbe5 !important; @@ -86,35 +91,50 @@ input[type="file"]::file-selector-button { } /* Badge - kolory pasujące do ciemnych alertów */ -.badge.bg-success, .badge.text-bg-success { +.badge.bg-success, +.badge.text-bg-success { background-color: #225d36 !important; color: #eaffea !important; } -.badge.bg-danger, .badge.text-bg-danger { + +.badge.bg-danger, +.badge.text-bg-danger { background-color: #7a1f23 !important; color: #ffeaea !important; } -.badge.bg-info, .badge.text-bg-info { + +.badge.bg-info, +.badge.text-bg-info { background-color: #1d3a4d !important; color: #eaf6ff !important; } -.badge.bg-warning, .badge.text-bg-warning { + +.badge.bg-warning, +.badge.text-bg-warning { background-color: #665c1e !important; color: #fffbe5 !important; } -.badge.bg-secondary, .badge.text-bg-secondary { + +.badge.bg-secondary, +.badge.text-bg-secondary { background-color: #343a40 !important; color: #e2e3e5 !important; } -.badge.bg-primary, .badge.text-bg-primary { + +.badge.bg-primary, +.badge.text-bg-primary { background-color: #184076 !important; color: #e6f0ff !important; } -.badge.bg-light, .badge.text-bg-light { + +.badge.bg-light, +.badge.text-bg-light { background-color: #444950 !important; color: #f8f9fa !important; } -.badge.bg-dark, .badge.text-bg-dark { + +.badge.bg-dark, +.badge.text-bg-dark { background-color: #181a1b !important; color: #f8f9fa !important; } @@ -157,6 +177,7 @@ input[type="checkbox"].large-checkbox:disabled::before { opacity: 0.5; cursor: not-allowed; } + input[type="checkbox"].large-checkbox:disabled { cursor: not-allowed; } @@ -223,6 +244,7 @@ input.form-control { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); @@ -232,11 +254,13 @@ input.form-control { #mass-add-list li.active { background: #198754 !important; color: #fff !important; - border: 1px solid #000000 !important; + border: 1px solid #000000 !important; } + #mass-add-list li { transition: background 0.2s; } + .quantity-input { width: 60px; background: #343a40; @@ -245,6 +269,7 @@ input.form-control { border-radius: 4px; text-align: center; } + .add-btn { margin-left: 10px; } @@ -256,6 +281,7 @@ input.form-control { justify-content: flex-end; gap: 4px; } + .list-group-item { display: flex; align-items: center; diff --git a/static/js/clickable_row.js b/static/js/clickable_row.js index a928fa5..955c80c 100644 --- a/static/js/clickable_row.js +++ b/static/js/clickable_row.js @@ -1,6 +1,6 @@ document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll('.clickable-item').forEach(item => { - item.addEventListener('click', function(e) { + item.addEventListener('click', function (e) { if (!e.target.closest('button') && e.target.tagName.toLowerCase() !== 'input') { const checkbox = this.querySelector('input[type="checkbox"]'); diff --git a/static/js/expenses.js b/static/js/expenses.js index 7b6b569..9c4b338 100644 --- a/static/js/expenses.js +++ b/static/js/expenses.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { let expensesChart = null; const rangeLabel = document.getElementById("chartRangeLabel"); @@ -8,57 +8,57 @@ document.addEventListener("DOMContentLoaded", function() { url += `&start_date=${startDate}&end_date=${endDate}`; } - fetch(url, {cache: "no-store"}) - .then(response => response.json()) - .then(data => { - const ctx = document.getElementById('expensesChart').getContext('2d'); + fetch(url, { cache: "no-store" }) + .then(response => response.json()) + .then(data => { + const ctx = document.getElementById('expensesChart').getContext('2d'); - if (expensesChart) { - expensesChart.destroy(); - } + if (expensesChart) { + expensesChart.destroy(); + } - expensesChart = new Chart(ctx, { - type: 'bar', - data: { - labels: data.labels, - datasets: [{ - label: 'Suma wydatków [PLN]', - data: data.expenses, - backgroundColor: '#0d6efd' - }] - }, - options: { - scales: { - y: { - beginAtZero: true + expensesChart = new Chart(ctx, { + type: 'bar', + data: { + labels: data.labels, + datasets: [{ + label: 'Suma wydatków [PLN]', + data: data.expenses, + backgroundColor: '#0d6efd' + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } } } + }); + + if (startDate && endDate) { + rangeLabel.textContent = `Widok: własny zakres (${startDate} → ${endDate})`; + } else { + let labelText = ""; + if (range === "monthly") labelText = "Widok: miesięczne"; + else if (range === "quarterly") labelText = "Widok: kwartalne"; + else if (range === "halfyearly") labelText = "Widok: półroczne"; + else if (range === "yearly") labelText = "Widok: roczne"; + rangeLabel.textContent = labelText; } + + }) + .catch(error => { + console.error("Błąd pobierania danych:", error); }); - - if (startDate && endDate) { - rangeLabel.textContent = `Widok: własny zakres (${startDate} → ${endDate})`; - } else { - let labelText = ""; - if (range === "monthly") labelText = "Widok: miesięczne"; - else if (range === "quarterly") labelText = "Widok: kwartalne"; - else if (range === "halfyearly") labelText = "Widok: półroczne"; - else if (range === "yearly") labelText = "Widok: roczne"; - rangeLabel.textContent = labelText; - } - - }) - .catch(error => { - console.error("Błąd pobierania danych:", error); - }); } - document.getElementById('loadExpensesBtn').addEventListener('click', function() { + document.getElementById('loadExpensesBtn').addEventListener('click', function () { loadExpenses(); }); document.querySelectorAll('.range-btn').forEach(btn => { - btn.addEventListener('click', function() { + btn.addEventListener('click', function () { document.querySelectorAll('.range-btn').forEach(b => b.classList.remove('active')); this.classList.add('active'); const range = this.getAttribute('data-range'); @@ -66,7 +66,7 @@ document.addEventListener("DOMContentLoaded", function() { }); }); - document.getElementById('customRangeBtn').addEventListener('click', function() { + document.getElementById('customRangeBtn').addEventListener('click', function () { const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; if (startDate && endDate) { @@ -78,7 +78,7 @@ document.addEventListener("DOMContentLoaded", function() { }); }); -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { const startDateInput = document.getElementById("startDate"); const endDateInput = document.getElementById("endDate"); diff --git a/static/js/functions.js b/static/js/functions.js index 8e57518..533a179 100644 --- a/static/js/functions.js +++ b/static/js/functions.js @@ -19,20 +19,6 @@ function updateItemState(itemId, isChecked) { applyHidePurchased(); } -/* function updateProgressBar() { - const items = document.querySelectorAll('#items li'); - const total = items.length; - const purchased = Array.from(items).filter(li => li.classList.contains('bg-success')).length; - const percent = total > 0 ? Math.round((purchased / total) * 100) : 0; - - const progressBar = document.getElementById('progress-bar'); - if (progressBar) { - progressBar.style.width = `${percent}%`; - progressBar.setAttribute('aria-valuenow', percent); - progressBar.textContent = `${percent}%`; - } -} */ - function updateProgressBar() { const items = document.querySelectorAll('#items li'); const total = items.length; @@ -286,87 +272,6 @@ function isListDifferent(oldItems, newItems) { return false; } -/* function updateListSmoothly(newItems) { - const itemsContainer = document.getElementById('items'); - const existingItemsMap = new Map(); - - Array.from(itemsContainer.querySelectorAll('li')).forEach(li => { - const id = parseInt(li.id.replace('item-', ''), 10); - existingItemsMap.set(id, li); - }); - - const fragment = document.createDocumentFragment(); - - newItems.forEach(item => { - let li = existingItemsMap.get(item.id); - let quantityBadge = ''; - if (item.quantity && item.quantity > 1) { - quantityBadge = `x${item.quantity}`; - } - - if (li) { - const checkbox = li.querySelector('input[type="checkbox"]'); - if (checkbox) { - checkbox.checked = item.purchased; - checkbox.disabled = false; - } - - li.classList.remove('bg-success', 'text-white', 'item-not-checked', 'opacity-50'); - if (item.purchased) { - li.classList.add('bg-success', 'text-white'); - } else { - li.classList.add('item-not-checked'); - } - - const nameSpan = li.querySelector(`#name-${item.id}`); - const expectedName = `${item.name} ${quantityBadge}`.trim(); - if (nameSpan && nameSpan.innerHTML.trim() !== expectedName) { - nameSpan.innerHTML = expectedName; - } - - let noteEl = li.querySelector('small'); - if (item.note) { - if (!noteEl) { - const newNote = document.createElement('small'); - newNote.className = 'text-danger ms-4'; - newNote.innerHTML = `[ ${item.note} ]`; - nameSpan.insertAdjacentElement('afterend', newNote); - } else { - noteEl.innerHTML = `[ ${item.note} ]`; - } - } else if (noteEl) { - noteEl.remove(); - } - - const sp = li.querySelector('.spinner-border'); - if (sp) sp.remove(); - - } else { - li = document.createElement('li'); - li.className = `list-group-item d-flex justify-content-between align-items-center flex-wrap ${item.purchased ? 'bg-success text-white' : 'item-not-checked'}`; - li.id = `item-${item.id}`; - - li.innerHTML = ` -
- - ${item.name} ${quantityBadge} - ${item.note ? `[ ${item.note} ]` : ''} -
- - `; - } - - fragment.appendChild(li); - }); - - itemsContainer.innerHTML = ''; - itemsContainer.appendChild(fragment); - - updateProgressBar(); - toggleEmptyPlaceholder(); - applyHidePurchased(); -} */ - function updateListSmoothly(newItems) { const itemsContainer = document.getElementById('items'); const existingItemsMap = new Map(); diff --git a/static/js/live.js b/static/js/live.js index 4bae47b..d48ae68 100644 --- a/static/js/live.js +++ b/static/js/live.js @@ -7,11 +7,11 @@ function toggleEmptyPlaceholder() { // prawdziwe
  • to te z data‑name lub id="item‑…" const hasRealItems = list.querySelector('li[data-name], li[id^="item-"]') !== null; - const placeholder = document.getElementById('empty-placeholder'); + const placeholder = document.getElementById('empty-placeholder'); if (!hasRealItems && !placeholder) { - const li = document.createElement('li'); - li.id = 'empty-placeholder'; + const li = document.createElement('li'); + li.id = 'empty-placeholder'; li.className = 'list-group-item bg-dark text-secondary text-center w-100'; li.textContent = 'Brak produktów w tej liście.'; list.appendChild(li); @@ -132,30 +132,30 @@ function setupList(listId, username) { const li = document.createElement('li'); li.className = 'list-group-item d-flex justify-content-between align-items-center flex-wrap item-not-checked'; li.id = `item-${data.id}`; - + let quantityBadge = ''; if (data.quantity && data.quantity > 1) { quantityBadge = `x${data.quantity}`; } li.innerHTML = ` -
    - - ${data.name} ${quantityBadge} -
    -
    - - -
    - `; +
    + + ${data.name} ${quantityBadge} +
    +
    + + +
    + `; -// #### WERSJA Z NAPISAMI #### -// -// - - document.getElementById('items').appendChild(li); - updateProgressBar(); - toggleEmptyPlaceholder(); + document.getElementById('items').prepend(li); }); socket.on('item_deleted', data => { @@ -168,7 +168,7 @@ function setupList(listId, username) { toggleEmptyPlaceholder(); }); - socket.on('progress_updated', function(data) { + socket.on('progress_updated', function (data) { const progressBar = document.getElementById('progress-bar'); if (progressBar) { progressBar.style.width = data.percent + '%'; diff --git a/static/js/notes.js b/static/js/notes.js index 5899c09..ed6a4c2 100644 --- a/static/js/notes.js +++ b/static/js/notes.js @@ -20,3 +20,4 @@ function submitNote(e) { modal.hide(); } } + diff --git a/static/js/product_suggestion.js b/static/js/product_suggestion.js index b6bec5f..cde7cf7 100644 --- a/static/js/product_suggestion.js +++ b/static/js/product_suggestion.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { // Odśwież eventy document.querySelectorAll('.sync-btn').forEach(btn => { btn.replaceWith(btn.cloneNode(true)); @@ -9,7 +9,7 @@ document.addEventListener("DOMContentLoaded", function() { // Synchronizacja sugestii document.querySelectorAll('.sync-btn').forEach(btn => { - btn.addEventListener('click', function(e) { + btn.addEventListener('click', function (e) { e.preventDefault(); const itemId = this.getAttribute('data-item-id'); @@ -22,28 +22,28 @@ document.addEventListener("DOMContentLoaded", function() { 'X-Requested-With': 'XMLHttpRequest' } }) - .then(response => response.json()) - .then(data => { - showToast(data.message, data.success ? 'success' : 'danger'); + .then(response => response.json()) + .then(data => { + showToast(data.message, data.success ? 'success' : 'danger'); - if (data.success) { - button.innerText = '✅ Zsynchronizowano'; - button.classList.remove('btn-outline-primary'); - button.classList.add('btn-success'); - } else { + if (data.success) { + button.innerText = '✅ Zsynchronizowano'; + button.classList.remove('btn-outline-primary'); + button.classList.add('btn-success'); + } else { + button.disabled = false; + } + }) + .catch(() => { + showToast('Błąd synchronizacji', 'danger'); button.disabled = false; - } - }) - .catch(() => { - showToast('Błąd synchronizacji', 'danger'); - button.disabled = false; - }); + }); }); }); // Usuwanie sugestii document.querySelectorAll('.delete-suggestion-btn').forEach(btn => { - btn.addEventListener('click', function(e) { + btn.addEventListener('click', function (e) { e.preventDefault(); const suggestionId = this.getAttribute('data-suggestion-id'); @@ -56,21 +56,21 @@ document.addEventListener("DOMContentLoaded", function() { 'X-Requested-With': 'XMLHttpRequest' } }) - .then(response => response.json()) - .then(data => { - showToast(data.message, data.success ? 'success' : 'danger'); + .then(response => response.json()) + .then(data => { + showToast(data.message, data.success ? 'success' : 'danger'); - if (data.success) { - const row = button.closest('tr'); - if (row) row.remove(); - } else { + if (data.success) { + const row = button.closest('tr'); + if (row) row.remove(); + } else { + button.disabled = false; + } + }) + .catch(() => { + showToast('Błąd usuwania sugestii', 'danger'); button.disabled = false; - } - }) - .catch(() => { - showToast('Błąd usuwania sugestii', 'danger'); - button.disabled = false; - }); + }); }); }); }); diff --git a/static/js/receipt_section.js b/static/js/receipt_section.js index 6681440..9f474e9 100644 --- a/static/js/receipt_section.js +++ b/static/js/receipt_section.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { const receiptSection = document.getElementById("receiptSection"); const toggleBtn = document.querySelector('[data-bs-target="#receiptSection"]'); diff --git a/static/js/sockets.js b/static/js/sockets.js index 18f418d..2a8f9a7 100644 --- a/static/js/sockets.js +++ b/static/js/sockets.js @@ -2,83 +2,83 @@ let didReceiveFirstFullList = false; // --- Automatyczny reconnect po powrocie do karty/przywróceniu internetu --- function reconnectIfNeeded() { - if (!socket.connected) { - socket.connect(); - } + if (!socket.connected) { + socket.connect(); + } } -document.addEventListener("visibilitychange", function() { - if (!document.hidden) { - reconnectIfNeeded(); - } +document.addEventListener("visibilitychange", function () { + if (!document.hidden) { + reconnectIfNeeded(); + } }); -window.addEventListener("focus", function() { - reconnectIfNeeded(); +window.addEventListener("focus", function () { + reconnectIfNeeded(); }); -window.addEventListener("online", function() { - reconnectIfNeeded(); +window.addEventListener("online", function () { + reconnectIfNeeded(); }); // --- Blokowanie checkboxów na czas reconnect --- function disableCheckboxes(disable) { - document.querySelectorAll('#items input[type="checkbox"]').forEach(cb => { - cb.disabled = disable; - }); + document.querySelectorAll('#items input[type="checkbox"]').forEach(cb => { + cb.disabled = disable; + }); } // --- Toasty przy rozłączeniu i połączeniu --- let firstConnect = true; let wasReconnected = false; // flaga do kontrolowania toasta -socket.on('connect', function() { - if (!firstConnect) { - //showToast('Połączono z serwerem!', 'info'); - disableCheckboxes(true); - wasReconnected = true; +socket.on('connect', function () { + if (!firstConnect) { + //showToast('Połączono z serwerem!', 'info'); + disableCheckboxes(true); + wasReconnected = true; - if (window.LIST_ID && window.usernameForReconnect) { - socket.emit('join_list', { room: window.LIST_ID, username: window.usernameForReconnect }); - } + if (window.LIST_ID && window.usernameForReconnect) { + socket.emit('join_list', { room: window.LIST_ID, username: window.usernameForReconnect }); } - firstConnect = false; + } + firstConnect = false; }); -socket.on('disconnect', function(reason) { - showToast('Utracono połączenie z serwerem...', 'warning'); - disableCheckboxes(true); +socket.on('disconnect', function (reason) { + showToast('Utracono połączenie z serwerem...', 'warning'); + disableCheckboxes(true); }); socket.off('joined_confirmation'); -socket.on('joined_confirmation', function(data) { - if (wasReconnected) { - showToast(`Lista: ${data.list_title} – ponownie dołączono.`, 'info'); - wasReconnected = false; - } - if (window.LIST_ID) { - socket.emit('request_full_list', { list_id: window.LIST_ID }); - } +socket.on('joined_confirmation', function (data) { + if (wasReconnected) { + showToast(`Lista: ${data.list_title} – ponownie dołączono.`, 'info'); + wasReconnected = false; + } + if (window.LIST_ID) { + socket.emit('request_full_list', { list_id: window.LIST_ID }); + } }); -socket.on('user_joined', function(data) { - showToast(`${data.username} dołączył do listy`, 'info'); +socket.on('user_joined', function (data) { + showToast(`${data.username} dołączył do listy`, 'info'); }); -socket.on('user_left', function(data) { - showToast(`${data.username} opuścił listę`, 'warning'); +socket.on('user_left', function (data) { + showToast(`${data.username} opuścił listę`, 'warning'); }); -socket.on('user_list', function(data) { - if (data.users.length > 0) { - const userList = data.users.join(', '); - showToast(`Obecni: ${userList}`, 'info'); - } +socket.on('user_list', function (data) { + if (data.users.length > 0) { + const userList = data.users.join(', '); + showToast(`Obecni: ${userList}`, 'info'); + } }); socket.on('receipt_added', function (data) { - + const gallery = document.getElementById("receiptGallery"); if (!gallery) return; diff --git a/static/js/toggle_button.js b/static/js/toggle_button.js index 5216365..d441f64 100644 --- a/static/js/toggle_button.js +++ b/static/js/toggle_button.js @@ -1,4 +1,4 @@ -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { const toggleBtn = document.getElementById("tempToggle"); const hiddenInput = document.getElementById("temporaryHidden"); @@ -23,7 +23,7 @@ document.addEventListener("DOMContentLoaded", function() { updateToggle(active); // Obsługa kliknięcia - toggleBtn.addEventListener("click", function() { + toggleBtn.addEventListener("click", function () { active = !active; toggleBtn.setAttribute("data-active", active ? "1" : "0"); hiddenInput.value = active ? "1" : "0"; diff --git a/static/js/user_management.js b/static/js/user_management.js index 8327c93..eee0339 100644 --- a/static/js/user_management.js +++ b/static/js/user_management.js @@ -1,4 +1,4 @@ -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', function () { var resetPasswordModal = document.getElementById('resetPasswordModal'); resetPasswordModal.addEventListener('show.bs.modal', function (event) { var button = event.relatedTarget; diff --git a/templates/admin/admin_panel.html b/templates/admin/admin_panel.html index 44624a7..3d85102 100644 --- a/templates/admin/admin_panel.html +++ b/templates/admin/admin_panel.html @@ -10,7 +10,8 @@
  • {% else %} -
  • - Brak produktów w tej liście. -
  • +
  • + Brak produktów w tej liście. +
  • {% endfor %} @@ -156,8 +154,10 @@ Lista: {{ list.title }}
    - - + +
    @@ -170,17 +170,18 @@ Lista: {{ list.title }}
    {% if receipt_files %} - {% for file in receipt_files %} -
    - - - -
    - {% endfor %} + {% for file in receipt_files %} +
    + + + +
    + {% endfor %} {% else %} - + {% endif %}
    @@ -205,14 +206,14 @@ Lista: {{ list.title }} {% block scripts %} + - - {% endblock %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/list_share.html b/templates/list_share.html index 8bb5ae5..cb0dec7 100644 --- a/templates/list_share.html +++ b/templates/list_share.html @@ -7,16 +7,16 @@ {% if list.is_archived %} - (Archiwalna) + (Archiwalna) {% endif %} {% if total_expense > 0 %} - - 💸 {{ '%.2f'|format(total_expense) }} PLN - + + 💸 {{ '%.2f'|format(total_expense) }} PLN + {% else %} - + {% endif %} @@ -28,106 +28,111 @@ {% if not list.is_archived %} -
    - - - -
    +
    + + + +
    {% endif %} {% if not list.is_archived %} -
    -
    💰 Dodaj wydatek
    -
    - - -
    +
    +
    💰 Dodaj wydatek
    +
    + + +
    {% endif %} -

    💸 Łącznie wydano: {{ '%.2f'|format(total_expense) }} PLN

    +

    💸 Łącznie wydano: {{ '%.2f'|format(total_expense) }} PLN

    -
    -{% set receipt_pattern = 'list_' ~ list.id %} + {% set receipt_pattern = 'list_' ~ list.id %} -
    📸 Paragony dodane do tej listy
    +
    📸 Paragony dodane do tej listy
    -
    - {% if receipt_files %} +
    + {% if receipt_files %} {% for file in receipt_files %} -
    - - - -
    +
    + + + +
    {% endfor %} - {% else %} + {% else %} - {% endif %} -
    + {% endif %} +
    -{% if not list.is_archived %} + {% if not list.is_archived %}
    📤 Dodaj zdjęcie paragonu
    -
    -
    -{% endif %} + {% endif %} @@ -150,7 +155,8 @@