poprawki w js

This commit is contained in:
Mateusz Gruszczyński
2025-07-16 09:04:01 +02:00
parent 53394469de
commit d3e50305a7
24 changed files with 779 additions and 817 deletions

View File

@@ -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;

View File

@@ -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"]');

View File

@@ -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");

View File

@@ -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 = `<span class="badge bg-secondary">x${item.quantity}</span>`;
}
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 = `[ <b>${item.note}</b> ]`;
nameSpan.insertAdjacentElement('afterend', newNote);
} else {
noteEl.innerHTML = `[ <b>${item.note}</b> ]`;
}
} 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 = `
<div class="d-flex align-items-center gap-3 flex-grow-1">
<input class="large-checkbox" type="checkbox" ${item.purchased ? 'checked' : ''}>
<span id="name-${item.id}" class="text-white">${item.name} ${quantityBadge}</span>
${item.note ? `<small class="text-danger ms-4">[ <b>${item.note}</b> ]</small>` : ''}
</div>
<button type="button" class="btn btn-sm btn-outline-info" onclick="openNoteModal(event, ${item.id})">📝</button>
`;
}
fragment.appendChild(li);
});
itemsContainer.innerHTML = '';
itemsContainer.appendChild(fragment);
updateProgressBar();
toggleEmptyPlaceholder();
applyHidePurchased();
} */
function updateListSmoothly(newItems) {
const itemsContainer = document.getElementById('items');
const existingItemsMap = new Map();

View File

@@ -7,11 +7,11 @@ function toggleEmptyPlaceholder() {
// prawdziwe <li> to te z dataname 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 = `<span class="badge bg-secondary">x${data.quantity}</span>`;
}
li.innerHTML = `
<div class="d-flex align-items-center flex-wrap gap-2">
<input class="large-checkbox" type="checkbox">
<span id="name-${data.id}" class="text-white">${data.name} ${quantityBadge}</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}', ${data.quantity || 1})">✏️</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteItem(${data.id})">🗑️</button>
</div>
`;
<div class="d-flex align-items-center flex-wrap gap-2 flex-grow-1">
<input class="large-checkbox" type="checkbox">
<span id="name-${data.id}" class="text-white">${data.name} ${quantityBadge}</span>
</div>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-warning"
onclick="editItem(${data.id}, '${data.name.replace(/'/g, "\\'")}', ${data.quantity || 1})">
✏️
</button>
<button type="button" class="btn btn-outline-danger"
onclick="deleteItem(${data.id})">
🗑️
</button>
</div>
`;
// #### WERSJA Z NAPISAMI ####
// <button class="btn btn-sm btn-outline-warning me-1" onclick="editItem(${data.id}, '${data.name}', ${data.quantity || 1})">✏️ Edytuj</button>
// <button class="btn btn-sm btn-outline-danger" onclick="deleteItem(${data.id})">🗑️ Usuń</button>
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 + '%';

View File

@@ -20,3 +20,4 @@ function submitNote(e) {
modal.hide();
}
}

View File

@@ -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;
});
});
});
});
});

View File

@@ -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"]');

View File

@@ -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;

View File

@@ -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";

View File

@@ -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;