duzo zmian i funkcji

This commit is contained in:
Mateusz Gruszczyński
2025-07-02 23:51:19 +02:00
parent 36cdcf175a
commit b02bd27aa1
10 changed files with 285 additions and 125 deletions

View File

@ -4,65 +4,110 @@ function setupList(listId, username) {
socket.emit('join_list', { room: listId, username: username });
const newItemInput = document.getElementById('newItem');
if (newItemInput) {
newItemInput.focus();
newItemInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
addItem(listId);
}
});
const parentDiv = newItemInput.closest('.input-group');
if (parentDiv) {
parentDiv.classList.add('position-relative');
}
const suggestionsBox = document.createElement('div');
suggestionsBox.className = 'list-group position-absolute w-100';
suggestionsBox.style.top = '100%';
suggestionsBox.style.left = '0';
suggestionsBox.style.zIndex = '9999';
newItemInput.parentNode.appendChild(suggestionsBox);
newItemInput.addEventListener('input', async function () {
const query = this.value;
if (query.length < 2) {
suggestionsBox.innerHTML = '';
return;
}
const res = await fetch(`/suggest_products?q=${encodeURIComponent(query)}`);
const data = await res.json();
suggestionsBox.innerHTML = '';
data.suggestions.forEach(s => {
const item = document.createElement('button');
item.type = 'button';
item.className = 'list-group-item list-group-item-action';
item.textContent = s;
item.onclick = () => {
newItemInput.value = s;
suggestionsBox.innerHTML = '';
newItemInput.focus();
};
suggestionsBox.appendChild(item);
});
});
newItemInput.addEventListener('blur', () => {
setTimeout(() => { suggestionsBox.innerHTML = ''; }, 200);
});
newItemInput.focus();
newItemInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
addItem(listId);
}
});
const itemsContainer = document.getElementById('items');
// Delegacja zdarzenia checkboxów
itemsContainer.addEventListener('change', function (e) {
if (e.target && e.target.type === 'checkbox') {
const li = e.target.closest('li');
if (li) {
const id = parseInt(li.id.replace('item-', ''), 10);
if (e.target.checked) {
socket.emit('check_item', { item_id: id });
} else {
socket.emit('uncheck_item', { item_id: id });
}
e.target.disabled = true;
li.classList.add('opacity-50');
let existingSpinner = li.querySelector('.spinner-border');
if (!existingSpinner) {
const spinner = document.createElement('span');
spinner.className = 'spinner-border spinner-border-sm ms-2';
spinner.setAttribute('role', 'status');
spinner.setAttribute('aria-hidden', 'true');
e.target.parentElement.appendChild(spinner);
}
}
}
});
socket.on('user_joined', data => showToast(`${data.username} dołączył do listy`));
socket.on('item_checked', data => {
updateItemState(data.item_id, true);
});
socket.on('item_unchecked', data => {
updateItemState(data.item_id, false);
});
socket.on('item_added', data => {
showToast(`${data.added_by} dodał: ${data.name}`);
const li = document.createElement('li');
li.className = 'list-group-item bg-dark text-white d-flex justify-content-between align-items-center flex-wrap';
li.className = 'list-group-item d-flex justify-content-between align-items-center flex-wrap bg-light text-dark';
li.id = `item-${data.id}`;
li.innerHTML = `
<div class="d-flex align-items-center flex-wrap gap-2">
<input type="checkbox">
<span id="name-${data.id}">${data.name}</span>
<span id="name-${data.id}" class="text-white">${data.name}</span>
</div>
<div class="mt-2 mt-md-0">
<button class="btn btn-sm btn-outline-warning me-1" onclick="editItem(${data.id}, '${data.name}')">✏️ Edytuj</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteItem(${data.id})">🗑️ Usuń</button>
</div>
`;
itemsContainer.appendChild(li);
});
socket.on('item_checked', data => {
const checkbox = document.querySelector(`#item-${data.item_id} input[type='checkbox']`);
if (checkbox) {
checkbox.checked = true;
}
});
socket.on('item_unchecked', data => {
const checkbox = document.querySelector(`#item-${data.item_id} input[type='checkbox']`);
if (checkbox) {
checkbox.checked = false;
}
document.getElementById('items').appendChild(li);
updateProgressBar();
});
socket.on('item_deleted', data => {
@ -71,6 +116,7 @@ function setupList(listId, username) {
li.remove();
}
showToast('Usunięto produkt');
updateProgressBar();
});
socket.on('item_edited', data => {
@ -80,6 +126,42 @@ function setupList(listId, username) {
}
showToast(`Zmieniono nazwę na: ${data.new_name}`);
});
updateProgressBar();
}
function updateItemState(itemId, isChecked) {
const checkbox = document.querySelector(`#item-${itemId} input[type='checkbox']`);
if (checkbox) {
checkbox.checked = isChecked;
checkbox.disabled = false;
const li = checkbox.closest('li');
li.classList.remove('opacity-50', 'bg-light', 'text-dark', 'bg-success', 'text-white');
if (isChecked) {
li.classList.add('bg-success', 'text-white');
} else {
li.classList.add('bg-light', 'text-dark');
}
const sp = li.querySelector('.spinner-border');
if (sp) sp.remove();
}
updateProgressBar();
}
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 addItem(listId) {
@ -124,5 +206,5 @@ function showToast(message) {
toast.setAttribute('role', 'alert');
toast.innerHTML = `<div class="d-flex"><div class="toast-body">${message}</div></div>`;
toastContainer.appendChild(toast);
setTimeout(() => { toast.remove(); }, 4000);
setTimeout(() => { toast.remove(); }, 1750);
}

12
static/js/toasts.js Normal file
View File

@ -0,0 +1,12 @@
function showToast(message, type = 'primary') {
const toastContainer = document.getElementById('toast-container');
if (!toastContainer) return;
const toast = document.createElement('div');
toast.className = `toast align-items-center text-bg-${type} border-0 show`;
toast.setAttribute('role', 'alert');
toast.innerHTML = `<div class="d-flex"><div class="toast-body">${message}</div></div>`;
toastContainer.appendChild(toast);
setTimeout(() => { toast.remove(); }, 4000);
}