From 5b73648f605509d0ec8d9bdff1d679f95e597a67 Mon Sep 17 00:00:00 2001 From: Alejandro Mantecon Rodriguez Date: Fri, 21 Nov 2025 12:05:30 +0100 Subject: [PATCH] fix: filtros de busqueda --- essenza/templates/base.html | 228 +++++++++++++ essenza/templates/product/catalog.html | 398 ++++++++++++++++++++--- essenza/templates/product/dashboard.html | 153 ++++++++- essenza/templates/product/list.html | 163 +++++++++- essenza/templates/product/stock.html | 156 ++++++++- 5 files changed, 1056 insertions(+), 42 deletions(-) diff --git a/essenza/templates/base.html b/essenza/templates/base.html index bed837f..32e9948 100644 --- a/essenza/templates/base.html +++ b/essenza/templates/base.html @@ -169,6 +169,54 @@ display: block; } + /* ====== FILTERS (shared styles for pages) ====== */ + .filters-bar { + max-width: 1100px; + margin: 16px auto 0; + display: flex; + gap: 20px; + align-items: center; + padding: 0 24px; + justify-content: center; + } + + .filter-select { + padding: 10px 16px; + border-radius: 24px; + background: linear-gradient(145deg, #ffffff 0%, #fef9f5 100%); + border: 2.5px solid #c06b3e; + color: #8b5a3c; + font-weight: 700; + font-size: 14px; + cursor: pointer; + box-shadow: 0 4px 12px rgba(192, 107, 62, 0.12), inset 0 1px 2px rgba(255,255,255,0.8); + appearance: none; + } + + .filter-select:focus { + outline: none; + box-shadow: 0 6px 20px rgba(192,107,62,0.18); + } + + .filter-select.small { + padding: 8px 12px; + border-radius: 12px; + font-size: 13px; + min-width: 160px; + } + + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + /* ====== ESCAPARATE ====== */ main { max-width: 1100px; @@ -261,6 +309,186 @@ } /* FIN ESTILOS AÑADIDOS */ + .filters { + max-width: 1100px; + margin: 25px auto 0 auto; + display: flex; + justify-content: center; + gap: 80px; + flex-wrap: wrap; + align-items: flex-start; + } + + /* Custom Dropdown Container */ + .custom-dropdown { + position: relative; + min-width: 220px; + max-width: 280px; + user-select: none; + flex-shrink: 0; + + } + + /* Dropdown Button */ + .dropdown-button { + padding: 12px 40px 12px 20px; + border-radius: 25px; + background: linear-gradient(145deg, #ffffff 0%, #fef9f5 100%); + border: 2.5px solid #c06b3e; + color: #8b5a3c; + font-weight: 700; + font-size: 14px; + letter-spacing: 0.5px; + cursor: pointer; + transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 12px rgba(192, 107, 62, 0.2), + inset 0 1px 2px rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + position: relative; + } + + .dropdown-button:hover { + border-color: #d77b46; + box-shadow: 0 6px 20px rgba(192, 107, 62, 0.35), + inset 0 1px 3px rgba(255, 255, 255, 0.9); + transform: translateY(-3px) scale(1.02); + background: linear-gradient(145deg, #ffffff 0%, #fff5ed 100%); + } + + .dropdown-button.active { + border-color: #c06b3e; + box-shadow: 0 8px 24px rgba(192, 107, 62, 0.4), + 0 0 0 4px rgba(192, 107, 62, 0.15); + background: #fff; + transform: translateY(-3px) scale(1.02); + } + + /* Dropdown Arrow */ + .dropdown-arrow { + width: 16px; + height: 16px; + position: absolute; + right: 16px; + transition: transform 0.3s ease; + stroke: #c06b3e; + stroke-width: 3.5; + } + + .dropdown-button.active .dropdown-arrow { + transform: rotate(180deg); + } + + /* Dropdown Menu */ + .dropdown-menu { + position: absolute; + top: calc(100% + 8px); + left: 0; + right: 0; + transform: translateY(-10px); + background: white; + border-radius: 20px; + border: 3px solid #c06b3e; + box-shadow: 0 12px 32px rgba(192, 107, 62, 0.25); + opacity: 0; + visibility: hidden; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1000; + overflow: hidden; + max-height: 300px; + overflow-y: auto; + white-space: nowrap; + } + + .dropdown-menu.show { + opacity: 1; + visibility: visible; + transform: translateY(0); + } + + /* Dropdown Items */ + .dropdown-item { + padding: 12px 28px; + color: #8b5a3c; + font-weight: 600; + font-size: 15px; + letter-spacing: 0.3px; + cursor: pointer; + transition: all 0.2s ease; + border-bottom: 1px solid #f5e6dc; + display: flex; + align-items: center; + justify-content: space-between; + } + + .dropdown-item:last-child { + border-bottom: none; + } + + .dropdown-item:hover { + background: linear-gradient(135deg, #fff5ed 0%, #ffeee0 100%); + color: #c06b3e; + padding-left: 32px; + } + + .dropdown-item.selected { + background: linear-gradient(135deg, #c06b3e 0%, #d77b46 100%); + color: #fff; + font-weight: 800; + } + + .dropdown-item.selected:hover { + background: linear-gradient(135deg, #d77b46 0%, #e88a55 100%); + padding-left: 28px; + } + + /* Check Icon for Selected Item */ + .dropdown-item .check-icon { + width: 18px; + height: 18px; + stroke: white; + stroke-width: 3; + opacity: 0; + transition: opacity 0.2s; + } + + .dropdown-item.selected .check-icon { + opacity: 1; + } + + /* Scrollbar styling */ + .dropdown-menu::-webkit-scrollbar { + width: 8px; + } + + .dropdown-menu::-webkit-scrollbar-track { + background: #fef9f5; + border-radius: 10px; + } + + .dropdown-menu::-webkit-scrollbar-thumb { + background: #c06b3e; + border-radius: 10px; + } + + .dropdown-menu::-webkit-scrollbar-thumb:hover { + background: #d77b46; + } + + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + /* Responsive */ @media (max-width: 768px) { header { diff --git a/essenza/templates/product/catalog.html b/essenza/templates/product/catalog.html index 30747e2..4a4d727 100644 --- a/essenza/templates/product/catalog.html +++ b/essenza/templates/product/catalog.html @@ -36,24 +36,179 @@ margin: 25px auto 0 auto; display: flex; justify-content: center; - gap: 12px; + gap: 80px; flex-wrap: wrap; + align-items: flex-start; } - .filter-btn { - padding: 10px 18px; - border-radius: 20px; + /* Custom Dropdown Container */ + .custom-dropdown { + position: relative; + min-width: 220px; + max-width: 280px; + user-select: none; + flex-shrink: 0; + + } + + /* Dropdown Button */ + .dropdown-button { + padding: 12px 40px 12px 20px; + border-radius: 25px; + background: linear-gradient(145deg, #ffffff 0%, #fef9f5 100%); + border: 2.5px solid #c06b3e; + color: #8b5a3c; + font-weight: 700; + font-size: 14px; + letter-spacing: 0.5px; + cursor: pointer; + transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 12px rgba(192, 107, 62, 0.2), + inset 0 1px 2px rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + position: relative; + } + + .dropdown-button:hover { + border-color: #d77b46; + box-shadow: 0 6px 20px rgba(192, 107, 62, 0.35), + inset 0 1px 3px rgba(255, 255, 255, 0.9); + transform: translateY(-3px) scale(1.02); + background: linear-gradient(145deg, #ffffff 0%, #fff5ed 100%); + } + + .dropdown-button.active { + border-color: #c06b3e; + box-shadow: 0 8px 24px rgba(192, 107, 62, 0.4), + 0 0 0 4px rgba(192, 107, 62, 0.15); background: #fff; - border: 2px solid #c06b3e; - color: #c06b3e; - font-weight: bold; + transform: translateY(-3px) scale(1.02); + } + + /* Dropdown Arrow */ + .dropdown-arrow { + width: 16px; + height: 16px; + position: absolute; + right: 16px; + transition: transform 0.3s ease; + stroke: #c06b3e; + stroke-width: 3.5; + } + + .dropdown-button.active .dropdown-arrow { + transform: rotate(180deg); + } + + /* Dropdown Menu */ + .dropdown-menu { + position: absolute; + top: calc(100% + 8px); + left: 0; + right: 0; + transform: translateY(-10px); + background: white; + border-radius: 20px; + border: 3px solid #c06b3e; + box-shadow: 0 12px 32px rgba(192, 107, 62, 0.25); + opacity: 0; + visibility: hidden; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1000; + overflow: hidden; + max-height: 300px; + overflow-y: auto; + white-space: nowrap; + } + + .dropdown-menu.show { + opacity: 1; + visibility: visible; + transform: translateY(0); + } + + /* Dropdown Items */ + .dropdown-item { + padding: 12px 28px; + color: #8b5a3c; + font-weight: 600; + font-size: 15px; + letter-spacing: 0.3px; cursor: pointer; - transition: 0.25s; + transition: all 0.2s ease; + border-bottom: 1px solid #f5e6dc; + display: flex; + align-items: center; + justify-content: space-between; } - .filter-btn.active, - .filter-btn:hover { + + .dropdown-item:last-child { + border-bottom: none; + } + + .dropdown-item:hover { + background: linear-gradient(135deg, #fff5ed 0%, #ffeee0 100%); + color: #c06b3e; + padding-left: 32px; + } + + .dropdown-item.selected { + background: linear-gradient(135deg, #c06b3e 0%, #d77b46 100%); + color: #fff; + font-weight: 800; + } + + .dropdown-item.selected:hover { + background: linear-gradient(135deg, #d77b46 0%, #e88a55 100%); + padding-left: 28px; + } + + /* Check Icon for Selected Item */ + .dropdown-item .check-icon { + width: 18px; + height: 18px; + stroke: white; + stroke-width: 3; + opacity: 0; + transition: opacity 0.2s; + } + + .dropdown-item.selected .check-icon { + opacity: 1; + } + + /* Scrollbar styling */ + .dropdown-menu::-webkit-scrollbar { + width: 8px; + } + + .dropdown-menu::-webkit-scrollbar-track { + background: #fef9f5; + border-radius: 10px; + } + + .dropdown-menu::-webkit-scrollbar-thumb { background: #c06b3e; - color: white; + border-radius: 10px; + } + + .dropdown-menu::-webkit-scrollbar-thumb:hover { + background: #d77b46; + } + + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } /* ===== GRID ===== */ @@ -113,15 +268,67 @@

Catálogo Essenza

Explora nuestra selección de productos mejor valorados

- +
- - - - - +
+ + +
+ + +
+ + +
@@ -131,7 +338,8 @@

Catálogo Essenza

class="card" data-name="{{ product.name|lower }}" data-category="{{ product.category }}" - onclick="location.href='{% url 'catalog_detail' product.pk %}'" + data-brand="{{ product.brand|default:''|lower }}" + data-url="{% url 'catalog_detail' product.pk %}" > {% if product.photo %} @@ -168,36 +376,148 @@

{{ product.name }}

} }; - /* ========== FILTROS ========== */ - const filterButtons = document.querySelectorAll(".filter-btn"); + /* ========== FILTROS (Custom Dropdowns + Brands dinámicas) ========== */ + const dropdownButton = document.getElementById("dropdownButton"); + const dropdownMenu = document.getElementById("dropdownMenu"); + const categoryItems = document.querySelectorAll("#dropdownMenu .dropdown-item"); + const selectedValue = document.getElementById("selectedValue"); + + const brandButton = document.getElementById("brandDropdownButton"); + const brandMenu = document.getElementById("brandDropdownMenu"); + const brandSelected = document.getElementById("brandSelected"); + const cards = document.querySelectorAll(".card"); - filterButtons.forEach((btn) => { - btn.addEventListener("click", () => { - filterButtons.forEach((b) => b.classList.remove("active")); - btn.classList.add("active"); + // State + let selectedCategory = 'all'; + let selectedBrand = 'all'; + + // Utility: apply all filters (category + brand + search) + function applyFilters() { + const searchInput = document.getElementById("searchInput"); + const searchText = (searchInput?.value || '').toLowerCase(); + + // Read selected category & brand from DOM to avoid stale state + const selectedCategoryItem = document.querySelector('#dropdownMenu .dropdown-item.selected'); + const selectedBrandItem = document.querySelector('#brandDropdownMenu .dropdown-item.selected'); + const currentCategory = (selectedCategoryItem?.dataset.value || 'all').toLowerCase(); + const currentBrand = (selectedBrandItem?.dataset.value || 'all').toLowerCase(); + + cards.forEach((card) => { + const name = (card.dataset.name || '').toLowerCase(); + const category = (card.dataset.category || '').toLowerCase(); + const brand = (card.dataset.brand || '').toLowerCase(); + + const matchCategory = currentCategory === 'all' || category === currentCategory; + const matchBrand = currentBrand === 'all' || brand === currentBrand || currentBrand === ''; + const matchSearch = !searchText || name.includes(searchText); + + card.style.display = (matchCategory && matchBrand && matchSearch) ? 'block' : 'none'; + }); + } + + // Category dropdown toggle + dropdownButton.addEventListener("click", (e) => { + e.stopPropagation(); + dropdownButton.classList.toggle("active"); + dropdownMenu.classList.toggle("show"); + }); + + // Category selection + categoryItems.forEach((item) => { + item.addEventListener('click', (e) => { + e.stopPropagation(); + categoryItems.forEach(i => i.classList.remove('selected')); + item.classList.add('selected'); + selectedCategory = item.dataset.value; + selectedValue.textContent = item.querySelector('span').textContent; + dropdownButton.classList.remove('active'); + dropdownMenu.classList.remove('show'); + applyFilters(); + }); + }); + + // Brand dropdown: build unique brands from cards + function populateBrandMenu() { + const brandSet = new Set(); + cards.forEach(c => { + const b = (c.dataset.brand || '').trim(); + if (b) brandSet.add(b.toLowerCase()); + }); + + // Remove existing brand items except the first (Todas las marcas) + const existing = Array.from(brandMenu.querySelectorAll('.dropdown-item')); + existing.slice(1).forEach(n => n.remove()); - const category = btn.dataset.category; - cards.forEach((card) => { - const match = - category === "all" || card.dataset.category === category; - card.style.display = match ? "block" : "none"; + // Sort and add + Array.from(brandSet).sort().forEach((b) => { + const el = document.createElement('div'); + el.className = 'dropdown-item'; + el.dataset.value = b; + const span = document.createElement('span'); + // capitalize first letter for display + span.textContent = b.charAt(0).toUpperCase() + b.slice(1); + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('class', 'check-icon'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('fill', 'none'); + svg.innerHTML = ''; + el.appendChild(span); + el.appendChild(svg); + brandMenu.appendChild(el); + }); + + // Attach listeners to the new items + const brandItems = brandMenu.querySelectorAll('.dropdown-item'); + brandItems.forEach((item) => { + item.addEventListener('click', (e) => { + e.stopPropagation(); + brandItems.forEach(i => i.classList.remove('selected')); + item.classList.add('selected'); + selectedBrand = item.dataset.value; + brandSelected.textContent = item.querySelector('span').textContent; + brandButton.classList.remove('active'); + brandMenu.classList.remove('show'); + applyFilters(); }); }); + } + + // Brand dropdown toggle + brandButton.addEventListener('click', (e) => { + e.stopPropagation(); + brandButton.classList.toggle('active'); + brandMenu.classList.toggle('show'); }); - /* ========== BUSCADOR ========== */ - const searchInput = document.getElementById("searchInput"); + // Close dropdowns when clicking outside + document.addEventListener('click', (e) => { + if (!e.target.closest('.custom-dropdown')) { + dropdownButton.classList.remove('active'); + dropdownMenu.classList.remove('show'); + brandButton.classList.remove('active'); + brandMenu.classList.remove('show'); + } + }); - searchInput.addEventListener("input", function () { - const searchText = this.value.toLowerCase(); + // Populate brands now + populateBrandMenu(); - cards.forEach((card) => { - const name = card.dataset.name; - const visible = name.includes(searchText); - card.style.display = visible ? "block" : "none"; + // Navigation: use data-url on cards + cards.forEach(card => { + card.addEventListener('click', (e) => { + const url = card.dataset.url; + if (url) window.location.href = url; }); }); + + /* ========== BUSCADOR ========== */ + const searchInput = document.getElementById("searchInput"); + if (searchInput) { + searchInput.addEventListener("input", function () { + applyFilters(); + }); + } diff --git a/essenza/templates/product/dashboard.html b/essenza/templates/product/dashboard.html index 9f3bb97..d50e9d9 100644 --- a/essenza/templates/product/dashboard.html +++ b/essenza/templates/product/dashboard.html @@ -189,9 +189,70 @@

Top Bestsellers

{% endif %} + +
+
+ + +
+ +
+ + +
+
+
{% if products %} {% for p in products %} -
+
{% if p.photo %} @@ -244,6 +305,96 @@

Top Bestsellers

} } }; + // Custom dropdown para dashboard.html + (function(){ + const categoryButton = document.getElementById('categoryButtonDash'); + const categoryMenu = document.getElementById('categoryMenuDash'); + const categorySelected = document.getElementById('categorySelectedDash'); + const brandButton = document.getElementById('brandButtonDash'); + const brandMenu = document.getElementById('brandMenuDash'); + const brandSelected = document.getElementById('brandSelectedDash'); + const cards = Array.from(document.querySelectorAll('.product-card')); + + // Toggle dropdowns + categoryButton.addEventListener('click', (e) => { + e.stopPropagation(); + categoryMenu.classList.toggle('show'); + brandMenu.classList.remove('show'); + }); + + brandButton.addEventListener('click', (e) => { + e.stopPropagation(); + brandMenu.classList.toggle('show'); + categoryMenu.classList.remove('show'); + }); + + document.addEventListener('click', () => { + categoryMenu.classList.remove('show'); + brandMenu.classList.remove('show'); + }); + + // Poblar marcas dinámicamente + function populateBrands() { + const brands = new Set(); + cards.forEach(c => { + const b = (c.dataset.brand || '').trim(); + if (b) brands.add(b); + }); + + const sortedBrands = Array.from(brands).sort(); + sortedBrands.forEach(b => { + const item = document.createElement('div'); + item.className = 'dropdown-item'; + item.dataset.value = b; + item.innerHTML = ` + ${b.charAt(0).toUpperCase() + b.slice(1)} + + + + `; + brandMenu.appendChild(item); + }); + } + populateBrands(); + + // Manejar selección de categoría + categoryMenu.addEventListener('click', (e) => { + const item = e.target.closest('.dropdown-item'); + if (!item) return; + + categoryMenu.querySelectorAll('.dropdown-item').forEach(i => i.classList.remove('selected')); + item.classList.add('selected'); + categorySelected.textContent = item.querySelector('span').textContent; + categoryMenu.classList.remove('show'); + applyFilters(); + }); + + // Manejar selección de marca + brandMenu.addEventListener('click', (e) => { + const item = e.target.closest('.dropdown-item'); + if (!item) return; + + brandMenu.querySelectorAll('.dropdown-item').forEach(i => i.classList.remove('selected')); + item.classList.add('selected'); + brandSelected.textContent = item.querySelector('span').textContent; + brandMenu.classList.remove('show'); + applyFilters(); + }); + + // Aplicar filtros + function applyFilters() { + const catSelected = categoryMenu.querySelector('.dropdown-item.selected'); + const brandSelItem = brandMenu.querySelector('.dropdown-item.selected'); + const cat = catSelected ? catSelected.dataset.value : 'all'; + const brand = brandSelItem ? brandSelItem.dataset.value : 'all'; + + cards.forEach(c => { + const matchCat = cat === 'all' || (c.dataset.category || '') === cat; + const matchBrand = brand === 'all' || (c.dataset.brand || '') === brand; + c.style.display = (matchCat && matchBrand) ? '' : 'none'; + }); + } + })(); diff --git a/essenza/templates/product/list.html b/essenza/templates/product/list.html index 2fd5dc7..2aaeefa 100644 --- a/essenza/templates/product/list.html +++ b/essenza/templates/product/list.html @@ -18,6 +18,14 @@ padding: 20px; background: transparent; } + + .filters { + max-width: 1100px; + margin: 25px auto 0 auto; + display: flex; + margin-bottom: 30px; + } + h1 { color: #c06b3e; text-align: center; @@ -166,10 +174,71 @@

Productos Essenza

>
+ +
+
+ + +
+ +
+ + +
+
+ {% if products %}
{% for product in products %} -
+
{% if product.photo %} {{ product.name }} @@ -217,6 +286,98 @@

Productos Essenza

{% endif %} +