feat: implement product search, media library, and vendor customers

- Add full-text product search in ProductService.search_products()
  searching titles, descriptions, SKUs, brands, and GTINs
- Implement complete vendor media library with file uploads,
  thumbnails, folders, and product associations
- Implement vendor customers API with listing, details, orders,
  statistics, and status management
- Add shop search results UI with pagination and add-to-cart
- Add vendor media library UI with drag-drop upload and grid view
- Add database migration for media_files and product_media tables
- Update TODO file with current launch status (~95% complete)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 21:32:59 +01:00
parent 4c2f7f1121
commit 8ee3f91467
16 changed files with 2456 additions and 102 deletions

View File

@@ -11,7 +11,7 @@ Vendor Context: require_vendor_context() - detects vendor from URL/subdomain/dom
import logging
from fastapi import APIRouter, Depends, Path, Query
from fastapi import APIRouter, Depends, Path, Query, Request
from sqlalchemy.orm import Session
from app.core.database import get_db
@@ -119,6 +119,7 @@ def get_product_details(
@router.get("/products/search", response_model=ProductListResponse) # public
def search_products(
request: Request,
q: str = Query(..., min_length=1, description="Search query"),
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=100),
@@ -128,7 +129,7 @@ def search_products(
"""
Search products in current vendor's catalog.
Searches in product names, descriptions, and SKUs.
Searches in product names, descriptions, SKUs, brands, and GTINs.
Vendor is automatically determined from request context (URL/subdomain/domain).
No authentication required.
@@ -137,6 +138,9 @@ def search_products(
- skip: Number of results to skip (pagination)
- limit: Maximum number of results to return
"""
# Get preferred language from request (via middleware or default)
language = getattr(request.state, "language", "en")
logger.debug(
f"[SHOP_API] search_products: '{q}'",
extra={
@@ -145,13 +149,18 @@ def search_products(
"query": q,
"skip": skip,
"limit": limit,
"language": language,
},
)
# TODO: Implement full-text search functionality
# For now, return filtered products
products, total = product_service.get_vendor_products(
db=db, vendor_id=vendor.id, skip=skip, limit=limit, is_active=True
# Search products using the service
products, total = product_service.search_products(
db=db,
vendor_id=vendor.id,
query=q,
skip=skip,
limit=limit,
language=language,
)
return ProductListResponse(