From 86f1e16ef28bd7d81c927da8dad5144a239ecc14 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Fri, 21 Nov 2025 23:15:25 +0100 Subject: [PATCH] Fixing vendor dashboard area --- app/api/v1/vendor/__init__.py | 16 +- app/api/v1/vendor/analytics.py | 4 +- app/api/v1/vendor/auth.py | 7 +- app/api/v1/vendor/customers.py | 14 +- app/api/v1/vendor/dashboard.py | 32 +++- app/api/v1/vendor/inventory.py | 20 +-- app/api/v1/vendor/marketplace.py | 8 +- app/api/v1/vendor/media.py | 18 +- app/api/v1/vendor/notifications.py | 22 +-- app/api/v1/vendor/orders.py | 22 ++- app/api/v1/vendor/payments.py | 18 +- app/api/v1/vendor/products.py | 18 +- app/api/v1/vendor/profile.py | 6 +- app/api/v1/vendor/settings.py | 6 +- app/api/v1/vendor/team.py | 6 +- app/services/stats_service.py | 5 +- app/templates/vendor/admin/customers.html | 11 -- app/templates/vendor/admin/dashboard.html | 158 ------------------ app/templates/vendor/admin/inventory.html | 11 -- .../vendor/admin/marketplace/browse.html | 11 -- .../vendor/admin/marketplace/config.html | 11 -- .../vendor/admin/marketplace/imports.html | 11 -- .../vendor/admin/marketplace/selected.html | 11 -- app/templates/vendor/admin/media.html | 11 -- app/templates/vendor/admin/notifications.html | 11 -- app/templates/vendor/admin/orders.html | 11 -- app/templates/vendor/admin/payments.html | 11 -- app/templates/vendor/admin/products.html | 11 -- app/templates/vendor/admin/settings.html | 11 -- app/templates/vendor/admin/teams.html | 11 -- app/templates/vendor/partials/header.html | 64 ++++--- app/templates/vendor/partials/sidebar.html | 11 ++ mkdocs.yml | 2 + static/vendor/js/dashboard.js | 48 +++++- static/vendor/js/init-alpine.js | 29 +++- static/vendor/js/login.js | 4 +- tests/fixtures/auth_fixtures.py | 31 ++++ tests/fixtures/vendor_fixtures.py | 33 ++++ 38 files changed, 312 insertions(+), 433 deletions(-) delete mode 100644 app/templates/vendor/admin/customers.html delete mode 100644 app/templates/vendor/admin/dashboard.html delete mode 100644 app/templates/vendor/admin/inventory.html delete mode 100644 app/templates/vendor/admin/marketplace/browse.html delete mode 100644 app/templates/vendor/admin/marketplace/config.html delete mode 100644 app/templates/vendor/admin/marketplace/imports.html delete mode 100644 app/templates/vendor/admin/marketplace/selected.html delete mode 100644 app/templates/vendor/admin/media.html delete mode 100644 app/templates/vendor/admin/notifications.html delete mode 100644 app/templates/vendor/admin/orders.html delete mode 100644 app/templates/vendor/admin/payments.html delete mode 100644 app/templates/vendor/admin/products.html delete mode 100644 app/templates/vendor/admin/settings.html delete mode 100644 app/templates/vendor/admin/teams.html diff --git a/app/api/v1/vendor/__init__.py b/app/api/v1/vendor/__init__.py index a0a3b436..6083191d 100644 --- a/app/api/v1/vendor/__init__.py +++ b/app/api/v1/vendor/__init__.py @@ -40,18 +40,19 @@ router = APIRouter() # ============================================================================ # These routes return JSON and are mounted at /api/v1/vendor/* -# Vendor info endpoint - must be first to handle GET /{vendor_code} -router.include_router(info.router, tags=["vendor-info"]) +# IMPORTANT: Specific routes MUST be registered BEFORE catch-all routes +# The info.router has GET /{vendor_code} which catches everything, +# so it must be registered LAST -# Authentication +# Authentication (no prefix, specific routes like /auth/login) router.include_router(auth.router, tags=["vendor-auth"]) -# Vendor management +# Vendor management (with prefixes: /dashboard/*, /profile/*, /settings/*) router.include_router(dashboard.router, tags=["vendor-dashboard"]) router.include_router(profile.router, tags=["vendor-profile"]) router.include_router(settings.router, tags=["vendor-settings"]) -# Business operations +# Business operations (with prefixes: /products/*, /orders/*, etc.) router.include_router(products.router, tags=["vendor-products"]) router.include_router(orders.router, tags=["vendor-orders"]) router.include_router(customers.router, tags=["vendor-customers"]) @@ -59,10 +60,13 @@ router.include_router(team.router, tags=["vendor-team"]) router.include_router(inventory.router, tags=["vendor-inventory"]) router.include_router(marketplace.router, tags=["vendor-marketplace"]) -# Services +# Services (with prefixes: /payments/*, /media/*, etc.) router.include_router(payments.router, tags=["vendor-payments"]) router.include_router(media.router, tags=["vendor-media"]) router.include_router(notifications.router, tags=["vendor-notifications"]) router.include_router(analytics.router, tags=["vendor-analytics"]) +# Vendor info endpoint - MUST BE LAST! Has catch-all GET /{vendor_code} +router.include_router(info.router, tags=["vendor-info"]) + __all__ = ["router"] diff --git a/app/api/v1/vendor/analytics.py b/app/api/v1/vendor/analytics.py index 69266e8a..4e99270c 100644 --- a/app/api/v1/vendor/analytics.py +++ b/app/api/v1/vendor/analytics.py @@ -7,7 +7,7 @@ import logging from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.stats_service import stats_service @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) def get_vendor_analytics( period: str = Query("30d", description="Time period: 7d, 30d, 90d, 1y"), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get vendor analytics data for specified time period.""" diff --git a/app/api/v1/vendor/auth.py b/app/api/v1/vendor/auth.py index 67832a18..127355bc 100644 --- a/app/api/v1/vendor/auth.py +++ b/app/api/v1/vendor/auth.py @@ -198,18 +198,15 @@ def vendor_logout(response: Response): @router.get("/me") def get_current_vendor_user( - request: Request, + user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db) ): """ Get current authenticated vendor user. This endpoint can be called to verify authentication and get user info. + Requires Authorization header (header-only authentication for API endpoints). """ - - # This will check both cookie and header - user = get_current_vendor_api(request, db=db) - return { "id": user.id, "username": user.username, diff --git a/app/api/v1/vendor/customers.py b/app/api/v1/vendor/customers.py index 1156d0c3..2727a017 100644 --- a/app/api/v1/vendor/customers.py +++ b/app/api/v1/vendor/customers.py @@ -9,7 +9,7 @@ from typing import Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from models.database.user import User @@ -26,7 +26,7 @@ def get_vendor_customers( search: Optional[str] = Query(None), is_active: Optional[bool] = Query(None), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -51,7 +51,7 @@ def get_vendor_customers( def get_customer_details( customer_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -72,7 +72,7 @@ def get_customer_details( def get_customer_orders( customer_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -94,7 +94,7 @@ def update_customer( customer_id: int, customer_data: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -114,7 +114,7 @@ def update_customer( def toggle_customer_status( customer_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -134,7 +134,7 @@ def toggle_customer_status( def get_customer_statistics( customer_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ diff --git a/app/api/v1/vendor/dashboard.py b/app/api/v1/vendor/dashboard.py index 27eb7ac1..9e8cc066 100644 --- a/app/api/v1/vendor/dashboard.py +++ b/app/api/v1/vendor/dashboard.py @@ -4,10 +4,10 @@ Vendor dashboard and statistics endpoints. """ import logging -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Request from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.stats_service import stats_service @@ -20,8 +20,8 @@ logger = logging.getLogger(__name__) @router.get("/stats") def get_vendor_dashboard_stats( - vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + request: Request, + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -32,7 +32,31 @@ def get_vendor_dashboard_stats( - Total orders - Total customers - Revenue metrics + + Vendor is determined from the authenticated user's vendor_user association. + Requires Authorization header (API endpoint). """ + # Get vendor from authenticated user's vendor_user record + from models.database.vendor import VendorUser + vendor_user = db.query(VendorUser).filter( + VendorUser.user_id == current_user.id + ).first() + + if not vendor_user: + from fastapi import HTTPException + raise HTTPException( + status_code=403, + detail="User is not associated with any vendor" + ) + + vendor = vendor_user.vendor + if not vendor or not vendor.is_active: + from fastapi import HTTPException + raise HTTPException( + status_code=404, + detail="Vendor not found or inactive" + ) + # Get vendor-scoped statistics stats_data = stats_service.get_vendor_stats(db=db, vendor_id=vendor.id) diff --git a/app/api/v1/vendor/inventory.py b/app/api/v1/vendor/inventory.py index 91dc2420..65b2d331 100644 --- a/app/api/v1/vendor/inventory.py +++ b/app/api/v1/vendor/inventory.py @@ -5,7 +5,7 @@ from typing import List, Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.inventory_service import inventory_service @@ -29,7 +29,7 @@ logger = logging.getLogger(__name__) def set_inventory( inventory: InventoryCreate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Set exact inventory quantity (replaces existing).""" @@ -40,7 +40,7 @@ def set_inventory( def adjust_inventory( adjustment: InventoryAdjust, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Adjust inventory (positive to add, negative to remove).""" @@ -51,7 +51,7 @@ def adjust_inventory( def reserve_inventory( reservation: InventoryReserve, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Reserve inventory for an order.""" @@ -62,7 +62,7 @@ def reserve_inventory( def release_reservation( reservation: InventoryReserve, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Release reserved inventory (cancel order).""" @@ -73,7 +73,7 @@ def release_reservation( def fulfill_reservation( reservation: InventoryReserve, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Fulfill reservation (complete order, remove from stock).""" @@ -84,7 +84,7 @@ def fulfill_reservation( def get_product_inventory( product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get inventory summary for a product.""" @@ -98,7 +98,7 @@ def get_vendor_inventory( location: Optional[str] = Query(None), low_stock: Optional[int] = Query(None, ge=0), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get all inventory for vendor.""" @@ -122,7 +122,7 @@ def update_inventory( inventory_id: int, inventory_update: InventoryUpdate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Update inventory entry.""" @@ -133,7 +133,7 @@ def update_inventory( def delete_inventory( inventory_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Delete inventory entry.""" diff --git a/app/api/v1/vendor/marketplace.py b/app/api/v1/vendor/marketplace.py index d57be0e5..cb98d980 100644 --- a/app/api/v1/vendor/marketplace.py +++ b/app/api/v1/vendor/marketplace.py @@ -10,7 +10,7 @@ from typing import List, Optional from fastapi import APIRouter, BackgroundTasks, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context # IMPORTANT from app.services.marketplace_import_job_service import marketplace_import_job_service @@ -33,7 +33,7 @@ async def import_products_from_marketplace( request: MarketplaceImportJobRequest, background_tasks: BackgroundTasks, vendor: Vendor = Depends(require_vendor_context()), # ADDED: Vendor from middleware - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Import products from marketplace CSV with background processing (Protected).""" @@ -79,7 +79,7 @@ async def import_products_from_marketplace( def get_marketplace_import_status( job_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get status of marketplace import job (Protected).""" @@ -99,7 +99,7 @@ def get_marketplace_import_jobs( skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=100), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get marketplace import jobs for current vendor (Protected).""" diff --git a/app/api/v1/vendor/media.py b/app/api/v1/vendor/media.py index e3ccf272..d90ee1fe 100644 --- a/app/api/v1/vendor/media.py +++ b/app/api/v1/vendor/media.py @@ -9,7 +9,7 @@ from typing import Optional from fastapi import APIRouter, Depends, Query, UploadFile, File from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from models.database.user import User @@ -26,7 +26,7 @@ def get_media_library( media_type: Optional[str] = Query(None, description="image, video, document"), search: Optional[str] = Query(None), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -53,7 +53,7 @@ async def upload_media( file: UploadFile = File(...), folder: Optional[str] = Query(None, description="products, general, etc."), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -79,7 +79,7 @@ async def upload_multiple_media( files: list[UploadFile] = File(...), folder: Optional[str] = Query(None), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -102,7 +102,7 @@ async def upload_multiple_media( def get_media_details( media_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -123,7 +123,7 @@ def update_media_metadata( media_id: int, metadata: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -144,7 +144,7 @@ def update_media_metadata( def delete_media( media_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -166,7 +166,7 @@ def delete_media( def get_media_usage( media_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -188,7 +188,7 @@ def get_media_usage( def optimize_media( media_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ diff --git a/app/api/v1/vendor/notifications.py b/app/api/v1/vendor/notifications.py index 835bd536..2889a9e7 100644 --- a/app/api/v1/vendor/notifications.py +++ b/app/api/v1/vendor/notifications.py @@ -9,7 +9,7 @@ from typing import Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from models.database.user import User @@ -25,7 +25,7 @@ def get_notifications( limit: int = Query(50, ge=1, le=100), unread_only: Optional[bool] = Query(False), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -48,7 +48,7 @@ def get_notifications( @router.get("/unread-count") def get_unread_count( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -68,7 +68,7 @@ def get_unread_count( def mark_as_read( notification_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -86,7 +86,7 @@ def mark_as_read( @router.put("/mark-all-read") def mark_all_as_read( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -105,7 +105,7 @@ def mark_all_as_read( def delete_notification( notification_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -123,7 +123,7 @@ def delete_notification( @router.get("/settings") def get_notification_settings( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -146,7 +146,7 @@ def get_notification_settings( def update_notification_settings( settings: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -165,7 +165,7 @@ def update_notification_settings( @router.get("/templates") def get_notification_templates( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -187,7 +187,7 @@ def update_notification_template( template_id: int, template_data: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -208,7 +208,7 @@ def update_notification_template( def send_test_notification( notification_data: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ diff --git a/app/api/v1/vendor/orders.py b/app/api/v1/vendor/orders.py index 6696f35f..49b5a360 100644 --- a/app/api/v1/vendor/orders.py +++ b/app/api/v1/vendor/orders.py @@ -6,10 +6,10 @@ Vendor order management endpoints. import logging from typing import Optional -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Query, Request, HTTPException from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.order_service import order_service @@ -20,7 +20,7 @@ from models.schema.order import ( OrderUpdate ) from models.database.user import User -from models.database.vendor import Vendor +from models.database.vendor import Vendor, VendorUser router = APIRouter(prefix="/orders") logger = logging.getLogger(__name__) @@ -33,7 +33,7 @@ def get_vendor_orders( status: Optional[str] = Query(None, description="Filter by order status"), customer_id: Optional[int] = Query(None, description="Filter by customer"), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -42,6 +42,8 @@ def get_vendor_orders( Supports filtering by: - status: Order status (pending, processing, shipped, delivered, cancelled) - customer_id: Filter orders from specific customer + + Requires Authorization header (API endpoint). """ orders, total = order_service.get_vendor_orders( db=db, @@ -64,10 +66,14 @@ def get_vendor_orders( def get_order_details( order_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): - """Get detailed order information including items and addresses.""" + """ + Get detailed order information including items and addresses. + + Requires Authorization header (API endpoint). + """ order = order_service.get_order( db=db, vendor_id=vendor.id, @@ -82,7 +88,7 @@ def update_order_status( order_id: int, order_update: OrderUpdate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -95,6 +101,8 @@ def update_order_status( - delivered: Order delivered - cancelled: Order cancelled - refunded: Order refunded + + Requires Authorization header (API endpoint). """ order = order_service.update_order_status( db=db, diff --git a/app/api/v1/vendor/payments.py b/app/api/v1/vendor/payments.py index c8b154cf..a25c7b69 100644 --- a/app/api/v1/vendor/payments.py +++ b/app/api/v1/vendor/payments.py @@ -8,7 +8,7 @@ import logging from fastapi import APIRouter, Depends from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from models.database.user import User @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) @router.get("/config") def get_payment_configuration( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -46,7 +46,7 @@ def get_payment_configuration( def update_payment_configuration( payment_config: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -67,7 +67,7 @@ def update_payment_configuration( def connect_stripe_account( stripe_data: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -87,7 +87,7 @@ def connect_stripe_account( @router.delete("/stripe/disconnect") def disconnect_stripe_account( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -106,7 +106,7 @@ def disconnect_stripe_account( @router.get("/methods") def get_payment_methods( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -125,7 +125,7 @@ def get_payment_methods( @router.get("/transactions") def get_payment_transactions( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -147,7 +147,7 @@ def get_payment_transactions( @router.get("/balance") def get_payment_balance( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -173,7 +173,7 @@ def refund_payment( payment_id: int, refund_data: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ diff --git a/app/api/v1/vendor/products.py b/app/api/v1/vendor/products.py index 527304ad..d229ca57 100644 --- a/app/api/v1/vendor/products.py +++ b/app/api/v1/vendor/products.py @@ -9,7 +9,7 @@ from typing import Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.product_service import product_service @@ -34,7 +34,7 @@ def get_vendor_products( is_active: Optional[bool] = Query(None), is_featured: Optional[bool] = Query(None), vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -65,7 +65,7 @@ def get_vendor_products( def get_product_details( product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get detailed product information including inventory.""" @@ -82,7 +82,7 @@ def get_product_details( def add_product_to_catalog( product_data: ProductCreate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -109,7 +109,7 @@ def update_product( product_id: int, product_data: ProductUpdate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Update product in vendor catalog.""" @@ -132,7 +132,7 @@ def update_product( def remove_product_from_catalog( product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Remove product from vendor catalog.""" @@ -154,7 +154,7 @@ def remove_product_from_catalog( def publish_from_marketplace( marketplace_product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """ @@ -185,7 +185,7 @@ def publish_from_marketplace( def toggle_product_active( product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Toggle product active status.""" @@ -208,7 +208,7 @@ def toggle_product_active( def toggle_product_featured( product_id: int, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Toggle product featured status.""" diff --git a/app/api/v1/vendor/profile.py b/app/api/v1/vendor/profile.py index 32df0155..b04f88bf 100644 --- a/app/api/v1/vendor/profile.py +++ b/app/api/v1/vendor/profile.py @@ -7,7 +7,7 @@ import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.vendor_service import vendor_service @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) @router.get("", response_model=VendorResponse) def get_vendor_profile( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get current vendor profile information.""" @@ -33,7 +33,7 @@ def get_vendor_profile( def update_vendor_profile( vendor_update: VendorUpdate, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Update vendor profile information.""" diff --git a/app/api/v1/vendor/settings.py b/app/api/v1/vendor/settings.py index 81324fce..7391bd91 100644 --- a/app/api/v1/vendor/settings.py +++ b/app/api/v1/vendor/settings.py @@ -7,7 +7,7 @@ import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session -from app.api.deps import get_current_user +from app.api.deps import get_current_vendor_api from app.core.database import get_db from middleware.vendor_context import require_vendor_context from app.services.vendor_service import vendor_service @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) @router.get("") def get_vendor_settings( vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Get vendor settings and configuration.""" @@ -46,7 +46,7 @@ def get_vendor_settings( def update_marketplace_settings( marketplace_config: dict, vendor: Vendor = Depends(require_vendor_context()), - current_user: User = Depends(get_current_user), + current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ): """Update marketplace integration settings.""" diff --git a/app/api/v1/vendor/team.py b/app/api/v1/vendor/team.py index 16d582c9..ad4108e2 100644 --- a/app/api/v1/vendor/team.py +++ b/app/api/v1/vendor/team.py @@ -18,7 +18,7 @@ from sqlalchemy.orm import Session from app.core.database import get_db from app.core.permissions import VendorPermissions from app.api.deps import ( - get_current_vendor_from_cookie_or_header, + get_current_vendor_api, require_vendor_owner, require_vendor_permission, get_user_permissions @@ -417,7 +417,7 @@ def list_roles( def get_my_permissions( request: Request, permissions: List[str] = Depends(get_user_permissions), - current_user: User = Depends(get_current_vendor_from_cookie_or_header) + current_user: User = Depends(get_current_vendor_api) ): """ Get current user's permissions in this vendor. @@ -431,6 +431,8 @@ def get_my_permissions( - Complete list of permissions - Whether user is owner - Role name (if team member) + + Requires Authorization header (API endpoint). """ vendor = request.state.vendor diff --git a/app/services/stats_service.py b/app/services/stats_service.py index 44486592..beabc8f1 100644 --- a/app/services/stats_service.py +++ b/app/services/stats_service.py @@ -74,8 +74,11 @@ class StatsService: ).count() # Staging statistics + # TODO: This is fragile - MarketplaceProduct uses vendor_name (string) not vendor_id + # Should add vendor_id foreign key to MarketplaceProduct for robust querying + # For now, matching by vendor name which could fail if names don't match exactly staging_products = db.query(MarketplaceProduct).filter( - MarketplaceProduct.vendor_id == vendor_id + MarketplaceProduct.vendor_name == vendor.name ).count() # Inventory statistics diff --git a/app/templates/vendor/admin/customers.html b/app/templates/vendor/admin/customers.html deleted file mode 100644 index a8f5c42e..00000000 --- a/app/templates/vendor/admin/customers.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Customer management - - - <-- Customer management --> - - diff --git a/app/templates/vendor/admin/dashboard.html b/app/templates/vendor/admin/dashboard.html deleted file mode 100644 index 8b5616d8..00000000 --- a/app/templates/vendor/admin/dashboard.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - Vendor Dashboard - Multi-Tenant Ecommerce Platform - - - - -
-
-

๐Ÿช

-
-
- - -
-
- - -
- - - - -
- -
- -
-
-
-

-

- โ€ข - -

-
-
- - -
-
-
- - -
-
-
-
Products
-
๐Ÿ“ฆ
-
-
-
in catalog
-
- -
-
-
Orders
-
๐Ÿ›’
-
-
-
total orders
-
- -
-
-
Customers
-
๐Ÿ‘ฅ
-
-
-
registered
-
- -
-
-
Revenue
-
๐Ÿ’ฐ
-
-
โ‚ฌ
-
total revenue
-
-
- - -
-

๐Ÿš€ Getting Started

-

- Welcome to your vendor dashboard! Start by importing products from the marketplace. -

- -
-
- - -
-
-

-

This section is coming soon in the next development slice.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/app/templates/vendor/admin/inventory.html b/app/templates/vendor/admin/inventory.html deleted file mode 100644 index 5753706c..00000000 --- a/app/templates/vendor/admin/inventory.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Inventory management - catalog products - - - <-- Inventory management - catalog products --> - - diff --git a/app/templates/vendor/admin/marketplace/browse.html b/app/templates/vendor/admin/marketplace/browse.html deleted file mode 100644 index 1f51208a..00000000 --- a/app/templates/vendor/admin/marketplace/browse.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Browse marketplace products - staging - - - <-- Browse marketplace products - staging --> - - diff --git a/app/templates/vendor/admin/marketplace/config.html b/app/templates/vendor/admin/marketplace/config.html deleted file mode 100644 index 293391dd..00000000 --- a/app/templates/vendor/admin/marketplace/config.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Marketplace configuration - - - <-- Marketplace configuration --> - - diff --git a/app/templates/vendor/admin/marketplace/imports.html b/app/templates/vendor/admin/marketplace/imports.html deleted file mode 100644 index eab10ee1..00000000 --- a/app/templates/vendor/admin/marketplace/imports.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Import jobs and history - - - <-- Import jobs and history --> - - diff --git a/app/templates/vendor/admin/marketplace/selected.html b/app/templates/vendor/admin/marketplace/selected.html deleted file mode 100644 index 6c41fe2f..00000000 --- a/app/templates/vendor/admin/marketplace/selected.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Selected products - pre-publish - - - <-- Selected products - pre-publish --> - - diff --git a/app/templates/vendor/admin/media.html b/app/templates/vendor/admin/media.html deleted file mode 100644 index 91f6506a..00000000 --- a/app/templates/vendor/admin/media.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Media library - - - <-- Media library --> - - diff --git a/app/templates/vendor/admin/notifications.html b/app/templates/vendor/admin/notifications.html deleted file mode 100644 index 4958e795..00000000 --- a/app/templates/vendor/admin/notifications.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Notification templates and logs - - - <-- Notification templates and logs --> - - diff --git a/app/templates/vendor/admin/orders.html b/app/templates/vendor/admin/orders.html deleted file mode 100644 index e9cefeee..00000000 --- a/app/templates/vendor/admin/orders.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Order management - - - <-- Order management --> - - diff --git a/app/templates/vendor/admin/payments.html b/app/templates/vendor/admin/payments.html deleted file mode 100644 index 9b5eee1d..00000000 --- a/app/templates/vendor/admin/payments.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Payment configuration - - - <-- Payment configuration --> - - diff --git a/app/templates/vendor/admin/products.html b/app/templates/vendor/admin/products.html deleted file mode 100644 index de610c02..00000000 --- a/app/templates/vendor/admin/products.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Catalog management - Product table - - - <-- Catalog management - Product table --> - - diff --git a/app/templates/vendor/admin/settings.html b/app/templates/vendor/admin/settings.html deleted file mode 100644 index c4e0c574..00000000 --- a/app/templates/vendor/admin/settings.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Vendor settings - - - <-- Vendor settings --> - - diff --git a/app/templates/vendor/admin/teams.html b/app/templates/vendor/admin/teams.html deleted file mode 100644 index 1e8811bf..00000000 --- a/app/templates/vendor/admin/teams.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Team management - - - <-- Team management --> - - diff --git a/app/templates/vendor/partials/header.html b/app/templates/vendor/partials/header.html index 7e227e0b..13089a48 100644 --- a/app/templates/vendor/partials/header.html +++ b/app/templates/vendor/partials/header.html @@ -67,10 +67,10 @@ -
  • +
  • - + +
  • diff --git a/app/templates/vendor/partials/sidebar.html b/app/templates/vendor/partials/sidebar.html index 0ed6e900..8fe9dac7 100644 --- a/app/templates/vendor/partials/sidebar.html +++ b/app/templates/vendor/partials/sidebar.html @@ -129,6 +129,17 @@ Follows same pattern as admin sidebar Team +
  • + + + + Profile + +