From ad5d179ba32ecbbb4786a376b3b86311a4a83279 Mon Sep 17 00:00:00 2001 From: Mike McGhen Date: Sat, 28 Mar 2026 13:02:13 -0400 Subject: [PATCH] feat: Add clear filters functionality and improve product filter options in dashboard --- dashboard/api.py | 7 +++++ dashboard/app.py | 7 ++++- dashboard/static/app.js | 55 ++++++++++++++++++++++++++-------- dashboard/static/style.css | 37 +++++++++++++++++++++++ dashboard/templates/index.html | 6 +++- 5 files changed, 98 insertions(+), 14 deletions(-) diff --git a/dashboard/api.py b/dashboard/api.py index 1e87daf..c9ea8f0 100644 --- a/dashboard/api.py +++ b/dashboard/api.py @@ -65,6 +65,13 @@ def get_stats(): """Get dashboard stats summary""" db = get_database() stats = db.get_dashboard_stats() + + # If no check in database, fall back to scraper state's last_check + if not stats.get('last_check'): + state = scraper_state.get_state() + if state.get('last_check'): + stats['last_check'] = {'checked_at': state['last_check']} + return jsonify(stats) diff --git a/dashboard/app.py b/dashboard/app.py index 7bfef02..7e7e97b 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -15,7 +15,12 @@ from flask_cors import CORS from src.database import get_database from src.favorites import get_favorites_manager -from .api import api_bp + +# Handle both direct execution and package import +try: + from .api import api_bp +except ImportError: + from api import api_bp # Create Flask app app = Flask(__name__) diff --git a/dashboard/static/app.js b/dashboard/static/app.js index 7671ad5..37d2a5d 100644 --- a/dashboard/static/app.js +++ b/dashboard/static/app.js @@ -86,6 +86,9 @@ function initEventListeners() { } }); + // Clear filters button + document.getElementById('clearFiltersBtn')?.addEventListener('click', clearAllFilters); + // Add favorite button document.getElementById('addFavoriteBtn')?.addEventListener('click', addFavorite); @@ -96,6 +99,16 @@ function initEventListeners() { }); } +// Clear all product filters +function clearAllFilters() { + document.getElementById('filterSite').value = ''; + document.getElementById('filterCategory').value = ''; + document.getElementById('filterStock').value = ''; + document.getElementById('filterFavorites').checked = false; + currentEventFilter = null; + loadProducts(); +} + // API Helpers async function api(endpoint, options = {}) { try { @@ -328,23 +341,23 @@ async function loadProducts() { const cleanName = cleanProductName(product.name); const hasImage = product.image_url && !product.image_url.includes('data:') && product.image_url.length > 10; - // Build store logo fallback HTML - const storeLogoHtml = ` -
- - -
- `; - return `
${hasImage - ? `` - : storeLogoHtml + ? `` + : `
+ + +
` } +
+ + +
${cleanName}
@@ -356,7 +369,7 @@ async function loadProducts() {
${storeInfo.name}
@@ -365,6 +378,24 @@ async function loadProducts() { }).join(''); } +// Handle product image errors by showing store icon +function handleProductImageError(img, site) { + const storeInfo = STORE_INFO[site] || { name: site, color: '#333', bgColor: '#666', logo: '', fallback: '?' }; + const container = img.parentElement; + img.style.display = 'none'; + + // Create fallback element + const fallback = document.createElement('div'); + fallback.className = 'product-store-icon'; + fallback.style.background = storeInfo.bgColor; + fallback.innerHTML = ` + + + `; + container.insertBefore(fallback, img); +} + async function showProductDetails(productId) { const product = await api(`/products/${productId}`); const history = await api(`/products/${productId}/history`); diff --git a/dashboard/static/style.css b/dashboard/static/style.css index c90a7f1..2afcd54 100644 --- a/dashboard/static/style.css +++ b/dashboard/static/style.css @@ -502,6 +502,13 @@ h3 { cursor: pointer; } +.btn-clear-filters { + margin-left: auto; + padding: 8px 16px !important; + margin-right: 0 !important; + margin-bottom: 0 !important; +} + /* Products Grid */ .products-grid { display: grid; @@ -545,6 +552,36 @@ h3 { align-items: center; justify-content: center; overflow: hidden; + position: relative; +} + +/* Store badge overlay on product images */ +.product-store-badge { + position: absolute; + bottom: 8px; + right: 8px; + width: 32px; + height: 32px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + z-index: 10; +} + +.store-badge-logo { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.store-badge-fallback { + font-size: 12px; + font-weight: 800; + align-items: center; + justify-content: center; } .product-image { diff --git a/dashboard/templates/index.html b/dashboard/templates/index.html index d1bb20a..0794fbf 100644 --- a/dashboard/templates/index.html +++ b/dashboard/templates/index.html @@ -80,8 +80,11 @@
Favorites Only +