From ee690e95c97fcd64198700ba4c013e376d4fbf34 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sat, 20 Dec 2025 22:34:55 +0100 Subject: [PATCH] fix: Letzshop page improvements - products, jobs, and tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Products tab now shows Letzshop marketplace products instead of vendor products - Uses /admin/products endpoint with marketplace=Letzshop filter - Fixed field names (image_link, price_numeric, sku vs vendor_sku) - Search now works with title, GTIN, SKU, brand 2. Jobs section moved to a separate tab - New "Jobs" tab between Exceptions and Settings - Tab watcher reloads data when switching tabs - Updated filter dropdown (removed export, added historical_import) 3. Product stats endpoint now accepts marketplace and vendor_name filters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- app/api/v1/admin/products.py | 6 +- app/services/marketplace_product_service.py | 63 +++++++++++++------ app/templates/admin/marketplace-letzshop.html | 13 ++-- .../admin/partials/letzshop-jobs-table.html | 10 +-- .../admin/partials/letzshop-products-tab.html | 22 +++---- static/admin/js/marketplace-letzshop.js | 43 ++++++++----- 6 files changed, 102 insertions(+), 55 deletions(-) diff --git a/app/api/v1/admin/products.py b/app/api/v1/admin/products.py index 0d64d4e1..6f54b14d 100644 --- a/app/api/v1/admin/products.py +++ b/app/api/v1/admin/products.py @@ -190,11 +190,15 @@ def get_products( @router.get("/stats", response_model=AdminProductStats) def get_product_stats( + marketplace: str | None = Query(None, description="Filter by marketplace"), + vendor_name: str | None = Query(None, description="Filter by vendor name"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """Get product statistics for admin dashboard.""" - stats = marketplace_product_service.get_admin_product_stats(db) + stats = marketplace_product_service.get_admin_product_stats( + db, marketplace=marketplace, vendor_name=vendor_name + ) return AdminProductStats(**stats) diff --git a/app/services/marketplace_product_service.py b/app/services/marketplace_product_service.py index bf555e3c..7cc85fad 100644 --- a/app/services/marketplace_product_service.py +++ b/app/services/marketplace_product_service.py @@ -681,36 +681,59 @@ class MarketplaceProductService: return result, total - def get_admin_product_stats(self, db: Session) -> dict: - """Get product statistics for admin dashboard.""" + def get_admin_product_stats( + self, + db: Session, + marketplace: str | None = None, + vendor_name: str | None = None, + ) -> dict: + """Get product statistics for admin dashboard. + + Args: + db: Database session + marketplace: Optional filter by marketplace (e.g., 'Letzshop') + vendor_name: Optional filter by vendor name + """ from sqlalchemy import func - total = db.query(func.count(MarketplaceProduct.id)).scalar() or 0 + # Build base filter + base_filters = [] + if marketplace: + base_filters.append(MarketplaceProduct.marketplace == marketplace) + if vendor_name: + base_filters.append(MarketplaceProduct.vendor_name == vendor_name) - active = ( - db.query(func.count(MarketplaceProduct.id)) - .filter(MarketplaceProduct.is_active == True) # noqa: E712 - .scalar() - or 0 + base_query = db.query(func.count(MarketplaceProduct.id)) + if base_filters: + base_query = base_query.filter(*base_filters) + + total = base_query.scalar() or 0 + + active_query = db.query(func.count(MarketplaceProduct.id)).filter( + MarketplaceProduct.is_active == True # noqa: E712 ) + if base_filters: + active_query = active_query.filter(*base_filters) + active = active_query.scalar() or 0 inactive = total - active - digital = ( - db.query(func.count(MarketplaceProduct.id)) - .filter(MarketplaceProduct.is_digital == True) # noqa: E712 - .scalar() - or 0 + digital_query = db.query(func.count(MarketplaceProduct.id)).filter( + MarketplaceProduct.is_digital == True # noqa: E712 ) + if base_filters: + digital_query = digital_query.filter(*base_filters) + digital = digital_query.scalar() or 0 physical = total - digital - marketplace_counts = ( - db.query( - MarketplaceProduct.marketplace, - func.count(MarketplaceProduct.id), - ) - .group_by(MarketplaceProduct.marketplace) - .all() + marketplace_query = db.query( + MarketplaceProduct.marketplace, + func.count(MarketplaceProduct.id), ) + if base_filters: + marketplace_query = marketplace_query.filter(*base_filters) + marketplace_counts = marketplace_query.group_by( + MarketplaceProduct.marketplace + ).all() by_marketplace = {mp or "unknown": count for mp, count in marketplace_counts} return { diff --git a/app/templates/admin/marketplace-letzshop.html b/app/templates/admin/marketplace-letzshop.html index c3c2b7b7..37daeffb 100644 --- a/app/templates/admin/marketplace-letzshop.html +++ b/app/templates/admin/marketplace-letzshop.html @@ -127,6 +127,9 @@ {{ tab_button('orders', 'Orders', tab_var='activeTab', icon='shopping-cart', count_var='orderStats.pending') }} {{ tab_button('exceptions', 'Exceptions', tab_var='activeTab', icon='exclamation-circle', count_var='exceptionStats.pending') }} + @@ -156,10 +159,12 @@ {% include 'admin/partials/letzshop-exceptions-tab.html' %} {{ endtab_panel() }} - -
- {% include 'admin/partials/letzshop-jobs-table.html' %} -
+ + diff --git a/app/templates/admin/partials/letzshop-jobs-table.html b/app/templates/admin/partials/letzshop-jobs-table.html index 7fd77f18..094f9da6 100644 --- a/app/templates/admin/partials/letzshop-jobs-table.html +++ b/app/templates/admin/partials/letzshop-jobs-table.html @@ -28,7 +28,7 @@ > - +