From 9ca2f8f7eaada59860caef1821a11dbb4194e61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 30 Mar 2026 14:49:41 +0200 Subject: [PATCH] fix in prev. --- shopping_app/static/css/style.css | 83 ++++++++-- shopping_app/static/js/preview_list_modal.js | 154 +++++++++++------- shopping_app/templates/admin/admin_panel.html | 4 +- .../templates/admin/edit_categories.html | 4 +- 4 files changed, 165 insertions(+), 80 deletions(-) diff --git a/shopping_app/static/css/style.css b/shopping_app/static/css/style.css index 8c562a8..115b098 100644 --- a/shopping_app/static/css/style.css +++ b/shopping_app/static/css/style.css @@ -54,15 +54,7 @@ /* ========================================================= Utilities & Sizes ========================================================= */ -/* - Main structure of this file: - 1. Design tokens / utilities - 2. Bootstrap overrides - 3. Forms / tables / toasts / modals - 4. Shared layout components - 5. Endpoint-specific sections - 6. Responsive fixes and hotfixes -*/ + .large-checkbox { width: 1.5em; height: 1.5em; @@ -192,7 +184,7 @@ input[type="file"]::file-selector-button { } /* ========================================================= - Forms (inputs, selects, switches, placeholders) + Forms ========================================================= */ .form-select, .form-control, @@ -4920,8 +4912,6 @@ body.sorting-active .shopping-item-row .large-checkbox { box-shadow: none !important; } - -/* v14 fixes: share/list action parity + sort handle visibility */ .endpoint-list_share .shopping-item-actions, .endpoint-shared_list .shopping-item-actions, .endpoint-view_list .shopping-item-actions, @@ -5603,7 +5593,6 @@ body:not(.sorting-active) .drag-handle { } } -/* --- Main page progress summary cards --- */ .endpoint-main_page #mainStatsCollapse.collapsing, .endpoint-main_page #mainStatsCollapse.show { overflow: visible; @@ -5729,3 +5718,71 @@ body:not(.sorting-active) .drag-handle { margin-top: 0 !important; border-top-width: 1px !important; } + +/* ========================================================= + Preview product list +========================================================= */ + +.preview-product-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.preview-product-summary { + padding: 0 0 0.85rem; + margin-bottom: 0.1rem; + border-bottom: 1px solid rgba(255,255,255,0.08); +} + +.preview-product-section { + display: flex; + flex-direction: column; + gap: 0.65rem; +} + +.preview-product-section-title { + margin: 0; + font-size: 1.05rem; + font-weight: 700; +} + +.preview-modal-items { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +#productPreviewModal .preview-modal-list-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + width: 100%; + min-width: 0; + padding: 0.9rem 1rem; + margin: 0 !important; + border-radius: 16px !important; + border: 1px solid rgba(255,255,255,0.08) !important; + background: linear-gradient(180deg, rgba(11,22,40,0.92) 0%, rgba(8,16,30,0.92) 100%) !important; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.03); +} + +#productPreviewModal .preview-modal-list-item:first-child, +#productPreviewModal .preview-modal-list-item:last-child, +#productPreviewModal .list-group-flush > .list-group-item:first-child, +#productPreviewModal .list-group-flush > .list-group-item:last-child { + border-radius: 16px !important; +} + +#productPreviewModal .preview-modal-list-item__name { + min-width: 0; + overflow-wrap: anywhere; + flex: 1 1 auto; +} + +#productPreviewModal .preview-modal-list-item .badge { + flex-shrink: 0; + min-width: 2.5rem; + border-radius: 10px; +} diff --git a/shopping_app/static/js/preview_list_modal.js b/shopping_app/static/js/preview_list_modal.js index e08036c..15e5349 100644 --- a/shopping_app/static/js/preview_list_modal.js +++ b/shopping_app/static/js/preview_list_modal.js @@ -1,6 +1,69 @@ document.addEventListener("DOMContentLoaded", function () { const modalElement = document.getElementById("productPreviewModal"); + if (!modalElement || typeof bootstrap === "undefined") return; + const modal = new bootstrap.Modal(modalElement); + const modalTitle = document.getElementById("previewModalLabel"); + const productList = document.getElementById("product-list"); + + if (!modalTitle || !productList) return; + + const renderState = (message, extraClass = "text-white") => { + productList.innerHTML = ""; + + const wrapper = document.createElement("div"); + wrapper.className = "preview-modal-items"; + + const item = document.createElement("div"); + item.className = `preview-modal-list-item ${extraClass}`.trim(); + item.textContent = message; + + wrapper.appendChild(item); + productList.appendChild(wrapper); + }; + + const createSection = (titleText) => { + const section = document.createElement("section"); + section.className = "preview-product-section"; + + const title = document.createElement("h6"); + title.className = "preview-product-section-title"; + title.textContent = titleText; + + const items = document.createElement("div"); + items.className = "preview-modal-items"; + + section.appendChild(title); + section.appendChild(items); + + return { section, items }; + }; + + const createItem = (itemData) => { + const row = document.createElement("div"); + row.className = "preview-modal-list-item"; + + const name = document.createElement("span"); + name.className = "preview-modal-list-item__name"; + name.textContent = itemData.name; + + const badge = document.createElement("span"); + badge.className = "badge"; + + if (itemData.purchased) { + badge.classList.add("bg-success"); + } else if (itemData.not_purchased) { + badge.classList.add("bg-warning", "text-dark"); + } else { + badge.classList.add("bg-secondary"); + } + + badge.textContent = `x${itemData.quantity}`; + + row.appendChild(name); + row.appendChild(badge); + return row; + }; modalElement.addEventListener("hidden.bs.modal", function () { document.querySelectorAll(".modal-backdrop").forEach((el) => el.remove()); @@ -11,101 +74,66 @@ document.addEventListener("DOMContentLoaded", function () { 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 = ` -
  • - ⏳ Ładowanie produktów... -
  • `; - + renderState("⏳ Ładowanie produktów..."); modal.show(); try { const res = await fetch(`/admin/list_items/${listId}`); + if (!res.ok) { + throw new Error(`HTTP ${res.status}`); + } + const data = await res.json(); + const totalCount = Number(data.total_count || 0); + const purchasedCount = Number(data.purchased_count || 0); + const totalExpense = Number(data.total_expense || 0); + const percent = totalCount > 0 ? Math.round((purchasedCount / totalCount) * 100) : 0; modalTitle.textContent = `🛒 ${data.title}`; productList.innerHTML = ""; - // 🔢 PODSUMOWANIE const summary = document.createElement("div"); - summary.className = "mb-3"; - - const percent = - data.total_count > 0 - ? Math.round((data.purchased_count / data.total_count) * 100) - : 0; - + summary.className = "preview-product-summary"; summary.innerHTML = ` -

    📦 ${data.total_count} produktów

    -

    ✅ Kupione: ${data.purchased_count} (${percent}%)

    -

    💸 Wydatek: ${data.total_expense.toFixed(2)} zł

    -
    - `; +

    📦 ${totalCount} produktów

    +

    ✅ Kupione: ${purchasedCount} (${percent}%)

    +

    💸 Wydatek: ${totalExpense.toFixed(2)} zł

    `; productList.appendChild(summary); - // 🛒 LISTY PRODUKTÓW - 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"; + const purchased = createSection("✔️ Kupione"); + const pending = createSection("🚫 Niekupione / Nieoznaczone"); let hasPurchased = false; - let hasUnpurchased = false; + let hasPending = 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 = ` - ${item.name} - - x${item.quantity} - `; + (data.items || []).forEach((item) => { + const row = createItem(item); if (item.purchased) { - purchasedList.appendChild(li); + purchased.items.appendChild(row); hasPurchased = true; } else { - notPurchasedList.appendChild(li); - hasUnpurchased = true; + pending.items.appendChild(row); + hasPending = true; } }); if (hasPurchased) { - const h5 = document.createElement("h6"); - h5.textContent = "✔️ Kupione"; - productList.appendChild(h5); - productList.appendChild(purchasedList); + productList.appendChild(purchased.section); } - if (hasUnpurchased) { - const h5 = document.createElement("h6"); - h5.textContent = "🚫 Niekupione / Nieoznaczone"; - productList.appendChild(h5); - productList.appendChild(notPurchasedList); + if (hasPending) { + productList.appendChild(pending.section); } - if (!hasPurchased && !hasUnpurchased) { - productList.innerHTML = ` -
  • - Brak produktów -
  • `; + if (!hasPurchased && !hasPending) { + renderState("Brak produktów", "text-muted fst-italic"); } - } catch (err) { + } catch (error) { modalTitle.textContent = "Błąd"; - productList.innerHTML = ` -
  • - ❌ Błąd podczas ładowania -
  • `; + renderState("❌ Błąd podczas ładowania", "text-danger"); } }); }); diff --git a/shopping_app/templates/admin/admin_panel.html b/shopping_app/templates/admin/admin_panel.html index 441b36b..2be0e6f 100644 --- a/shopping_app/templates/admin/admin_panel.html +++ b/shopping_app/templates/admin/admin_panel.html @@ -328,7 +328,7 @@ @@ -341,7 +341,7 @@ checkboxes.forEach(cb => cb.checked = this.checked); }); - + {% endblock %} {% endblock %} \ No newline at end of file diff --git a/shopping_app/templates/admin/edit_categories.html b/shopping_app/templates/admin/edit_categories.html index 5466d26..f259107 100644 --- a/shopping_app/templates/admin/edit_categories.html +++ b/shopping_app/templates/admin/edit_categories.html @@ -137,7 +137,7 @@ aria-label="Zamknij"> @@ -146,6 +146,6 @@ {% endblock %} {% block scripts %} - + {% endblock %} \ No newline at end of file