duzo poprawek ux i logicznych

This commit is contained in:
Mateusz Gruszczyński
2025-07-28 00:04:12 +02:00
parent a5025b94ff
commit b9b91ff82b
7 changed files with 68 additions and 44 deletions

2
app.py
View File

@@ -1147,6 +1147,7 @@ def view_list(list_id):
total_count = len(items)
purchased_count = len([i for i in items if i.purchased])
percent = (purchased_count / total_count * 100) if total_count > 0 else 0
is_owner = current_user.id == shopping_list.owner_id
return render_template(
"list.html",
@@ -1159,6 +1160,7 @@ def view_list(list_id):
expenses=expenses,
total_expense=total_expense,
is_share=False,
is_owner=is_owner
)

View File

@@ -281,6 +281,9 @@ function renderItem(item, isShare = window.IS_SHARE, showEditOnly = false) {
: 'item-not-checked'
}`;
const isOwner = window.IS_OWNER === true || window.IS_OWNER === 'true';
const allowEdit = !isShare || showEditOnly || isOwner;
let quantityBadge = '';
if (item.quantity && item.quantity > 1) {
quantityBadge = `<span class="badge bg-secondary">x${item.quantity}</span>`;
@@ -309,8 +312,8 @@ function renderItem(item, isShare = window.IS_SHARE, showEditOnly = false) {
let rightButtons = '';
// ✏️ i 🗑️ — tylko jeśli nie jesteśmy w trybie /share lub jesteśmy w 15s (tymczasowo)
if (!isShare || showEditOnly) {
// ✏️ i 🗑️ — tylko jeśli nie jesteśmy w trybie /share lub jesteśmy w 15s (tymczasowo) lub jesteśmy właścicielem
if (allowEdit) {
rightButtons += `
<button type="button" class="btn btn-outline-light"
onclick="editItem(${item.id}, '${item.name.replace(/'/g, "\\'")}', ${item.quantity || 1})">
@@ -332,7 +335,8 @@ function renderItem(item, isShare = window.IS_SHARE, showEditOnly = false) {
}
// ⚠️ tylko jeśli NIE jest oznaczony jako niekupiony i nie jesteśmy w 15s
if (!item.not_purchased && !showEditOnly) {
if (!item.not_purchased && (isOwner || (isShare && !showEditOnly))) {
rightButtons += `
<button type="button" class="btn btn-outline-light"
onclick="markNotPurchasedModal(event, ${item.id})">
@@ -341,7 +345,8 @@ function renderItem(item, isShare = window.IS_SHARE, showEditOnly = false) {
}
// 📝 tylko jeśli jesteśmy w /share i nie jesteśmy w 15s
if (isShare && !showEditOnly) {
if (isShare && !showEditOnly && !isOwner) {
rightButtons += `
<button type="button" class="btn btn-outline-light"
onclick="openNoteModal(event, ${item.id})">
@@ -349,7 +354,6 @@ function renderItem(item, isShare = window.IS_SHARE, showEditOnly = false) {
</button>`;
}
li.innerHTML = `${left}<div class="btn-group btn-group-sm" role="group">${rightButtons}</div>`;
if (item.added_by && item.owner_id && item.added_by_id && item.added_by_id !== item.owner_id) {

View File

@@ -205,43 +205,39 @@ function setupList(listId, username) {
});
socket.on('note_updated', data => {
const itemEl = document.getElementById(`item-${data.item_id}`);
if (itemEl) {
let noteEl = itemEl.querySelector('small');
if (noteEl) {
//noteEl.innerHTML = `[ Notatka: <b>${data.note}</b> ]`;
noteEl.innerHTML = `[ <b>${data.note}</b> ]`;
} else {
const newNote = document.createElement('small');
newNote.className = 'text-danger ms-4';
//newNote.innerHTML = `[ Notatka: <b>${data.note}</b> ]`;
newNote.innerHTML = `[ <b>${data.note}</b> ]`;
const idx = window.currentItems.findIndex(i => i.id === data.item_id);
if (idx !== -1) {
window.currentItems[idx].note = data.note;
const flexColumn = itemEl.querySelector('.d-flex.flex-column');
if (flexColumn) {
flexColumn.appendChild(newNote);
} else {
itemEl.appendChild(newNote);
}
const newItem = renderItem(window.currentItems[idx], true);
const oldItem = document.getElementById(`item-${data.item_id}`);
if (oldItem && newItem) {
oldItem.replaceWith(newItem);
}
}
showToast('Notatka dodana/zaktualizowana', 'success');
});
socket.on('item_edited', data => {
const nameSpan = document.getElementById(`name-${data.item_id}`);
if (nameSpan) {
let quantityBadge = '';
if (data.new_quantity && data.new_quantity > 1) {
quantityBadge = ` <span class="badge bg-secondary">x${data.new_quantity}</span>`;
}
nameSpan.innerHTML = `${data.new_name}${quantityBadge}`;
}
showToast(`Zaktualizowano produkt: ${data.new_name} (x${data.new_quantity})`, 'success');
});
updateProgressBar();
toggleEmptyPlaceholder();
socket.on('item_edited', data => {
const idx = window.currentItems.findIndex(i => i.id === data.item_id);
if (idx !== -1) {
window.currentItems[idx].name = data.new_name;
window.currentItems[idx].quantity = data.new_quantity;
const newItem = renderItem(window.currentItems[idx], true);
const oldItem = document.getElementById(`item-${data.item_id}`);
if (oldItem && newItem) {
oldItem.replaceWith(newItem);
}
}
showToast(`Zaktualizowano produkt: ${data.new_name} (x${data.new_quantity})`, 'success');
updateProgressBar();
toggleEmptyPlaceholder();
});
// --- WAŻNE: zapisz dane do reconnect ---
window.LIST_ID = listId;

View File

@@ -16,3 +16,24 @@ document.addEventListener("DOMContentLoaded", function () {
localStorage.setItem("receiptSectionOpen", "false");
});
});
document.addEventListener("DOMContentLoaded", function () {
const btn = document.getElementById("toggleReceiptBtn");
const target = document.querySelector(btn.getAttribute("data-bs-target"));
function updateUI() {
const isShown = target.classList.contains("show");
btn.innerHTML = isShown
? "📄 Ukryj sekcję paragonów"
: "📄 Pokaż sekcję paragonów";
btn.classList.toggle("active", isShown);
btn.classList.toggle("btn-outline-light", !isShown);
btn.classList.toggle("btn-secondary", isShown);
}
target.addEventListener("shown.bs.collapse", updateUI);
target.addEventListener("hidden.bs.collapse", updateUI);
updateUI();
});

View File

@@ -65,15 +65,17 @@
</div>
{% if orphan_files and request.path.endswith('/all') %}
<hr class="my-4">
<h4 class="mt-3 mb-2 text-warning">Znalezione nieprzypisane pliki ({{ orphan_files_count }})</h4>
<div class="row g-3">
{% for f in orphan_files %}
<div class="col-6 col-md-4 col-lg-3">
<div class="card bg-dark border-warning text-warning h-100">
<img src="{{ url_for('uploaded_file', filename=f) }}" class="card-img-top"
style="object-fit: cover; height: 200px;">
<a href="{{ url_for('uploaded_file', filename=f) }}" class="glightbox" data-gallery="receipts"
data-title="{{ r.filename }}">
<img src="{{ url_for('uploaded_file', filename=f) }}" class="card-img-top"
style="object-fit: cover; height: 200px;">
</a>
<div class="card-body text-center">
<p class="small mb-1 fw-bold">{{ f }}</p>
<div class="alert alert-warning small py-1 mb-2">Brak powiązania z listą!</div>
@@ -88,8 +90,6 @@
</div>
{% endif %}
<div class="modal fade" id="cropModal" tabindex="-1" aria-labelledby="cropModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content bg-dark text-white">

View File

@@ -215,6 +215,8 @@
const isShare = document.getElementById('items').dataset.isShare === 'true';
window.IS_SHARE = isShare;
window.LIST_ID = {{ list.id }};
window.IS_OWNER = {{ 'true' if is_owner else 'false' }};
</script>
<script src="{{ url_for('static_bp.serve_js', filename='mass_add.js') }}"></script>

View File

@@ -100,14 +100,13 @@
{% endif %}
<p id="total-expense2"><b>💸 Łącznie wydano:</b> {{ '%.2f'|format(total_expense) }} PLN</p>
<button class="btn btn-outline-light mb-3" type="button" data-bs-toggle="collapse" data-bs-target="#receiptSection"
aria-expanded="false" aria-controls="receiptSection">
<button id="toggleReceiptBtn" class="btn btn-outline-light mb-3 w-100 w-md-auto d-block mx-auto" type="button"
data-bs-toggle="collapse" data-bs-target="#receiptSection" aria-expanded="false" aria-controls="receiptSection">
📄 Pokaż sekcję paragonów
</button>
<div class="collapse" id="receiptSection">
<div class="collapse px-2 px-md-4" id="receiptSection">
{% set receipt_pattern = 'list_' ~ list.id %}
<hr>
<div class="mt-3 p-3 border border-secondary rounded bg-dark text-white {% if not receipt_files %}d-none{% endif %}"
id="receiptAnalysisBlock">
<h5>🧠 Analiza paragonów (OCR)</h5>