jedna kategoria dla listy

This commit is contained in:
Mateusz Gruszczyński
2025-08-13 22:46:14 +02:00
parent 479e601de1
commit f138cabd53
4 changed files with 159 additions and 3 deletions

20
app.py
View File

@@ -2971,6 +2971,26 @@ def admin_mass_edit_categories():
"admin/mass_edit_categories.html", lists=lists, categories=categories
)
@app.route("/admin/list_items/<int:list_id>")
@login_required
@admin_required
def admin_list_items_json(list_id):
l = db.session.get(ShoppingList, list_id)
if not l:
return jsonify({"error": "Lista nie istnieje"}), 404
items = [
{
"name": item.name,
"quantity": item.quantity,
"purchased": item.purchased,
"not_purchased": item.not_purchased,
}
for item in l.items
]
return jsonify({"title": l.title, "items": items})
@app.route("/healthcheck")
def healthcheck():

View File

@@ -1,4 +1,5 @@
document.addEventListener("DOMContentLoaded", function () {
// Inicjalizacja Tom Select dla wszystkich select[multiple]
document.querySelectorAll('select[multiple]').forEach(function (el) {
new TomSelect(el, {
plugins: ['remove_button'],
@@ -8,4 +9,76 @@ document.addEventListener("DOMContentLoaded", function () {
dropdownParent: 'body'
});
});
});
// Obsługa przycisków podglądu produktów
document.querySelectorAll(".preview-btn").forEach((btn) => {
btn.addEventListener("click", async () => {
const listId = btn.dataset.listId;
const modalTitle = document.getElementById("previewModalLabel");
const productList = document.getElementById("product-list");
modalTitle.textContent = "Ładowanie...";
productList.innerHTML = '<li class="list-group-item bg-dark text-white">⏳ Ładowanie produktów...</li>';
const modal = new bootstrap.Modal(document.getElementById("productPreviewModal"));
modal.show();
try {
const res = await fetch(`/admin/list_items/${listId}`);
const data = await res.json();
modalTitle.textContent = `🛒 ${data.title}`;
productList.innerHTML = "";
const purchasedList = document.createElement("ul");
purchasedList.className = "list-group list-group-flush mb-3";
const notPurchasedList = document.createElement("ul");
notPurchasedList.className = "list-group list-group-flush";
let hasPurchased = false;
let hasUnpurchased = false;
data.items.forEach(item => {
const li = document.createElement("li");
li.className = "list-group-item bg-dark text-white d-flex justify-content-between";
li.innerHTML = `
<span>${item.name}</span>
<span class="badge ${item.purchased ? 'bg-success' : item.not_purchased ? 'bg-warning text-dark' : 'bg-secondary'}">
x${item.quantity}
</span>`;
if (item.purchased) {
purchasedList.appendChild(li);
hasPurchased = true;
} else {
notPurchasedList.appendChild(li);
hasUnpurchased = true;
}
});
if (hasPurchased) {
const h5 = document.createElement("h6");
h5.textContent = "✔️ Kupione";
productList.appendChild(h5);
productList.appendChild(purchasedList);
}
if (hasUnpurchased) {
const h5 = document.createElement("h6");
h5.textContent = "🚫 Niekupione / Nieoznaczone";
productList.appendChild(h5);
productList.appendChild(notPurchasedList);
}
if (!hasPurchased && !hasUnpurchased) {
productList.innerHTML = '<li class="list-group-item bg-dark text-muted fst-italic">Brak produktów</li>';
}
} catch (err) {
modalTitle.textContent = "Błąd";
productList.innerHTML = '<li class="list-group-item bg-dark text-danger">❌ Błąd podczas ładowania</li>';
}
});
});
});

View File

@@ -0,0 +1,45 @@
document.addEventListener("DOMContentLoaded", function () {
// Tom Select jak był
// Obsługa kliknięcia "Podgląd"
document.querySelectorAll(".preview-btn").forEach((btn) => {
btn.addEventListener("click", async () => {
const listId = btn.dataset.listId;
const modalTitle = document.getElementById("previewModalLabel");
const productList = document.getElementById("product-list");
modalTitle.textContent = "Ładowanie...";
productList.innerHTML = '<li class="list-group-item bg-dark text-white">⏳ Ładowanie produktów...</li>';
const modal = new bootstrap.Modal(document.getElementById("productPreviewModal"));
modal.show();
try {
const res = await fetch(`/admin/list_items/${listId}`);
const data = await res.json();
modalTitle.textContent = `🛒 ${data.title}`;
productList.innerHTML = "";
if (data.items.length === 0) {
productList.innerHTML = '<li class="list-group-item bg-dark text-muted fst-italic">Brak produktów</li>';
} else {
data.items.forEach(item => {
const li = document.createElement("li");
li.className = "list-group-item bg-dark text-white d-flex justify-content-between";
li.innerHTML = `
<span>${item.name}</span>
<span class="badge ${item.purchased ? 'bg-success' : item.not_purchased ? 'bg-warning text-dark' : 'bg-secondary'}">
x${item.quantity}
</span>`;
productList.appendChild(li);
});
}
} catch (err) {
modalTitle.textContent = "Błąd";
productList.innerHTML = '<li class="list-group-item bg-dark text-danger">❌ Błąd podczas ładowania</li>';
}
});
});
});

View File

@@ -32,8 +32,10 @@
<td>{{ lst.owner.username if lst.owner else "?" }}</td>
<td>{{ lst.created_at.strftime('%Y-%m-%d') }}</td>
<td>
<a href="{{ url_for('view_list', list_id=lst.id) }}" target="_blank"
class="btn btn-sm btn-outline-light">🔗 Otwórz</a>
<button type="button" class="btn btn-sm btn-outline-info preview-btn"
data-list-id="{{ lst.id }}">
🔍 Podgląd
</button>
</td>
<td style="min-width: 220px;">
<select name="categories_{{ lst.id }}" multiple
@@ -58,6 +60,22 @@
</div>
</form>
<!-- Modal podglądu produktów -->
<div class="modal fade" id="productPreviewModal" tabindex="-1" aria-labelledby="previewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="previewModalLabel">Podgląd produktów</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Zamknij"></button>
</div>
<div class="modal-body">
<ul id="product-list" class="list-group list-group-flush"></ul>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js"></script>