duzo zmian i funkcji
This commit is contained in:
@ -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
12
static/js/toasts.js
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user