diff --git a/app.py b/app.py
index 7d8498b..e036b27 100644
--- a/app.py
+++ b/app.py
@@ -1543,7 +1543,7 @@ def handle_disconnect(sid):
@socketio.on("add_item")
def handle_add_item(data):
list_id = data["list_id"]
- name = data["name"]
+ name = data["name"].strip()
quantity = data.get("quantity", 1)
try:
@@ -1553,34 +1553,56 @@ def handle_add_item(data):
except:
quantity = 1
- new_item = Item(
- list_id=list_id,
- name=name,
- quantity=quantity,
- added_by=current_user.id if current_user.is_authenticated else None,
- )
- db.session.add(new_item)
+ # Szukamy istniejącego itemu w tej liście (ignorując wielkość liter)
+ existing_item = Item.query.filter(
+ Item.list_id == list_id,
+ func.lower(Item.name) == name.lower(),
+ Item.not_purchased == False
+ ).first()
- if not SuggestedProduct.query.filter_by(name=name).first():
- new_suggestion = SuggestedProduct(name=name)
- db.session.add(new_suggestion)
+ if existing_item:
+ existing_item.quantity += quantity
+ db.session.commit()
- db.session.commit()
+ emit(
+ "item_edited",
+ {
+ "item_id": existing_item.id,
+ "new_name": existing_item.name,
+ "new_quantity": existing_item.quantity
+ },
+ to=str(list_id),
+ )
+ else:
+ new_item = Item(
+ list_id=list_id,
+ name=name,
+ quantity=quantity,
+ added_by=current_user.id if current_user.is_authenticated else None,
+ )
+ db.session.add(new_item)
- emit(
- "item_added",
- {
- "id": new_item.id,
- "name": new_item.name,
- "quantity": new_item.quantity,
- "added_by": (
- current_user.username if current_user.is_authenticated else "Gość"
- ),
- },
- to=str(list_id),
- include_self=True,
- )
+ if not SuggestedProduct.query.filter(func.lower(SuggestedProduct.name) == name.lower()).first():
+ new_suggestion = SuggestedProduct(name=name)
+ db.session.add(new_suggestion)
+ db.session.commit()
+
+ emit(
+ "item_added",
+ {
+ "id": new_item.id,
+ "name": new_item.name,
+ "quantity": new_item.quantity,
+ "added_by": (
+ current_user.username if current_user.is_authenticated else "Gość"
+ ),
+ },
+ to=str(list_id),
+ include_self=True,
+ )
+
+ # Aktualizacja postępu
purchased_count, total_count, percent = get_progress(list_id)
emit(
diff --git a/static/js/functions.js b/static/js/functions.js
index 533a179..ac36d16 100644
--- a/static/js/functions.js
+++ b/static/js/functions.js
@@ -217,7 +217,7 @@ function applyHidePurchased(isInit = false) {
}
function toggleVisibility(listId) {
- fetch('/toggle_visibility/' + listId, {method: 'POST'})
+ fetch('/toggle_visibility/' + listId, { method: 'POST' })
.then(response => response.json())
.then(data => {
const shareHeader = document.getElementById('share-header');
@@ -297,10 +297,9 @@ function updateListSmoothly(newItems) {
}
// Klasy tła
- li.className = `list-group-item d-flex justify-content-between align-items-center flex-wrap clickable-item ${
- item.purchased ? 'bg-success text-white' :
+ li.className = `list-group-item d-flex justify-content-between align-items-center flex-wrap clickable-item ${item.purchased ? 'bg-success text-white' :
item.not_purchased ? 'bg-warning text-dark' : 'item-not-checked'
- }`;
+ }`;
// HTML wewnętrzny
li.innerHTML = `
@@ -317,7 +316,7 @@ function updateListSmoothly(newItems) {
${item.not_purchased ? `
-
@@ -334,11 +333,11 @@ function updateListSmoothly(newItems) {
` : ''}
`}
${!window.IS_SHARE ? `
-
-
@@ -358,7 +357,7 @@ function updateListSmoothly(newItems) {
}
-document.addEventListener("DOMContentLoaded", function() {
+document.addEventListener("DOMContentLoaded", function () {
const receiptSection = document.getElementById("receiptSection");
const toggleBtn = document.querySelector('[data-bs-target="#receiptSection"]');
@@ -377,14 +376,14 @@ document.addEventListener("DOMContentLoaded", function() {
});
});
-document.addEventListener("DOMContentLoaded", function() {
+document.addEventListener("DOMContentLoaded", function () {
const toggle = document.getElementById('hidePurchasedToggle');
if (!toggle) return;
const savedState = localStorage.getItem('hidePurchasedToggle');
toggle.checked = savedState === 'true';
applyHidePurchased(true);
- toggle.addEventListener('change', function() {
+ toggle.addEventListener('change', function () {
localStorage.setItem('hidePurchasedToggle', toggle.checked ? 'true' : 'false');
applyHidePurchased();
});
diff --git a/static/js/live.js b/static/js/live.js
index d48ae68..9cc05c3 100644
--- a/static/js/live.js
+++ b/static/js/live.js
@@ -144,11 +144,11 @@ function setupList(listId, username) {
${data.name} ${quantityBadge}
-
-
@@ -156,6 +156,7 @@ function setupList(listId, username) {
`;
document.getElementById('items').prepend(li);
+ toggleEmptyPlaceholder();
});
socket.on('item_deleted', data => {
diff --git a/static/js/mass_add.js b/static/js/mass_add.js
index 0265f35..495517e 100644
--- a/static/js/mass_add.js
+++ b/static/js/mass_add.js
@@ -2,11 +2,16 @@ document.addEventListener('DOMContentLoaded', function () {
const modal = document.getElementById('massAddModal');
const productList = document.getElementById('mass-add-list');
+ // Funkcja normalizacji (usuwa diakrytyki i zamienia na lowercase)
+ function normalize(str) {
+ return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
+ }
+
modal.addEventListener('show.bs.modal', async function () {
let addedProducts = new Set();
document.querySelectorAll('#items li').forEach(li => {
if (li.dataset.name) {
- addedProducts.add(li.dataset.name.toLowerCase());
+ addedProducts.add(normalize(li.dataset.name));
}
});
@@ -20,8 +25,7 @@ document.addEventListener('DOMContentLoaded', function () {
const li = document.createElement('li');
li.className = 'list-group-item d-flex justify-content-between align-items-center bg-dark text-light';
- if (addedProducts.has(name.toLowerCase())) {
- // Produkt już dodany — oznacz jako nieaktywny
+ if (addedProducts.has(normalize(name))) {
const nameSpan = document.createElement('span');
nameSpan.textContent = name;
li.appendChild(nameSpan);
@@ -32,17 +36,14 @@ document.addEventListener('DOMContentLoaded', function () {
badge.textContent = 'Dodano';
li.appendChild(badge);
} else {
- // Nazwa produktu
const nameSpan = document.createElement('span');
nameSpan.textContent = name;
nameSpan.style.flex = '1 1 auto';
li.appendChild(nameSpan);
- // Kontener na minus, pole i plus
const qtyWrapper = document.createElement('div');
qtyWrapper.className = 'd-flex align-items-center ms-2 quantity-controls';
- // Minus
const minusBtn = document.createElement('button');
minusBtn.type = 'button';
minusBtn.className = 'btn btn-outline-light btn-sm px-2';
@@ -51,18 +52,15 @@ document.addEventListener('DOMContentLoaded', function () {
qty.value = Math.max(1, parseInt(qty.value) - 1);
};
- // Pole ilości
const qty = document.createElement('input');
qty.type = 'number';
qty.min = 1;
qty.value = 1;
- qty.className = 'form-control text-center p-1';
- qty.classList.add('rounded');
+ qty.className = 'form-control text-center p-1 rounded';
qty.style.width = '50px';
qty.style.margin = '0 2px';
qty.title = 'Ilość';
- // Plus
const plusBtn = document.createElement('button');
plusBtn.type = 'button';
plusBtn.className = 'btn btn-outline-light btn-sm px-2';
@@ -75,7 +73,6 @@ document.addEventListener('DOMContentLoaded', function () {
qtyWrapper.appendChild(qty);
qtyWrapper.appendChild(plusBtn);
- // Przycisk dodania
const btn = document.createElement('button');
btn.className = 'btn btn-sm btn-primary ms-4';
btn.textContent = '+';
@@ -99,25 +96,19 @@ document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('#mass-add-list li').forEach(li => {
const itemName = li.firstChild.textContent.trim();
- if (itemName === data.name && !li.classList.contains('opacity-50')) {
- // Usuń wszystkie dzieci
+ if (normalize(itemName) === normalize(data.name) && !li.classList.contains('opacity-50')) {
while (li.firstChild) {
li.removeChild(li.firstChild);
}
- // Ustaw nazwę
li.textContent = data.name;
-
- // Dodaj klasę wyszarzenia
li.classList.add('opacity-50');
- // Dodaj badge
const badge = document.createElement('span');
badge.className = 'badge bg-success ms-auto';
badge.textContent = 'Dodano';
li.appendChild(badge);
- // Zablokuj kliknięcia
li.onclick = null;
}
});
diff --git a/templates/list.html b/templates/list.html
index 9f3c548..6c76190 100644
--- a/templates/list.html
+++ b/templates/list.html
@@ -84,7 +84,7 @@
-
+
{% for item in items %}
- Ukryj zaznaczone
-
+
{% for item in items %}
-
+ {% if item.purchased %}bg-success text-white{% elif item.not_purchased %}bg-warning text-dark{% else %}item-not-checked{% endif %}">