diff --git a/app/routes/shop_pages.py b/app/routes/shop_pages.py index 72281576..0f6c6d4b 100644 --- a/app/routes/shop_pages.py +++ b/app/routes/shop_pages.py @@ -240,7 +240,9 @@ async def shop_products_page(request: Request, db: Session = Depends(get_db)): "/products/{product_id}", response_class=HTMLResponse, include_in_schema=False ) async def shop_product_detail_page( - request: Request, product_id: int = Path(..., description="Product ID") + request: Request, + product_id: int = Path(..., description="Product ID"), + db: Session = Depends(get_db), ): """ Render product detail page. @@ -256,7 +258,7 @@ async def shop_product_detail_page( ) return templates.TemplateResponse( - "shop/product.html", get_shop_context(request, product_id=product_id) + "shop/product.html", get_shop_context(request, db=db, product_id=product_id) ) @@ -264,7 +266,9 @@ async def shop_product_detail_page( "/categories/{category_slug}", response_class=HTMLResponse, include_in_schema=False ) async def shop_category_page( - request: Request, category_slug: str = Path(..., description="Category slug") + request: Request, + category_slug: str = Path(..., description="Category slug"), + db: Session = Depends(get_db), ): """ Render category products page. @@ -280,12 +284,12 @@ async def shop_category_page( ) return templates.TemplateResponse( - "shop/category.html", get_shop_context(request, category_slug=category_slug) + "shop/category.html", get_shop_context(request, db=db, category_slug=category_slug) ) @router.get("/cart", response_class=HTMLResponse, include_in_schema=False) -async def shop_cart_page(request: Request): +async def shop_cart_page(request: Request, db: Session = Depends(get_db)): """ Render shopping cart page. Shows cart items and allows quantity updates. @@ -299,11 +303,11 @@ async def shop_cart_page(request: Request): }, ) - return templates.TemplateResponse("shop/cart.html", get_shop_context(request)) + return templates.TemplateResponse("shop/cart.html", get_shop_context(request, db=db)) @router.get("/checkout", response_class=HTMLResponse, include_in_schema=False) -async def shop_checkout_page(request: Request): +async def shop_checkout_page(request: Request, db: Session = Depends(get_db)): """ Render checkout page. Handles shipping, payment, and order confirmation. @@ -317,11 +321,11 @@ async def shop_checkout_page(request: Request): }, ) - return templates.TemplateResponse("shop/checkout.html", get_shop_context(request)) + return templates.TemplateResponse("shop/checkout.html", get_shop_context(request, db=db)) @router.get("/search", response_class=HTMLResponse, include_in_schema=False) -async def shop_search_page(request: Request): +async def shop_search_page(request: Request, db: Session = Depends(get_db)): """ Render search results page. Shows products matching search query. @@ -335,7 +339,7 @@ async def shop_search_page(request: Request): }, ) - return templates.TemplateResponse("shop/search.html", get_shop_context(request)) + return templates.TemplateResponse("shop/search.html", get_shop_context(request, db=db)) # ============================================================================ @@ -344,7 +348,7 @@ async def shop_search_page(request: Request): @router.get("/account/register", response_class=HTMLResponse, include_in_schema=False) -async def shop_register_page(request: Request): +async def shop_register_page(request: Request, db: Session = Depends(get_db)): """ Render customer registration page. No authentication required. @@ -359,12 +363,12 @@ async def shop_register_page(request: Request): ) return templates.TemplateResponse( - "shop/account/register.html", get_shop_context(request) + "shop/account/register.html", get_shop_context(request, db=db) ) @router.get("/account/login", response_class=HTMLResponse, include_in_schema=False) -async def shop_login_page(request: Request): +async def shop_login_page(request: Request, db: Session = Depends(get_db)): """ Render customer login page. No authentication required. @@ -379,14 +383,14 @@ async def shop_login_page(request: Request): ) return templates.TemplateResponse( - "shop/account/login.html", get_shop_context(request) + "shop/account/login.html", get_shop_context(request, db=db) ) @router.get( "/account/forgot-password", response_class=HTMLResponse, include_in_schema=False ) -async def shop_forgot_password_page(request: Request): +async def shop_forgot_password_page(request: Request, db: Session = Depends(get_db)): """ Render forgot password page. Allows customers to reset their password. @@ -401,7 +405,7 @@ async def shop_forgot_password_page(request: Request): ) return templates.TemplateResponse( - "shop/account/forgot-password.html", get_shop_context(request) + "shop/account/forgot-password.html", get_shop_context(request, db=db) ) @@ -752,7 +756,7 @@ async def generic_content_page( ) return templates.TemplateResponse( - "shop/content-page.html", get_shop_context(request, page=page) + "shop/content-page.html", get_shop_context(request, db=db, page=page) ) diff --git a/app/routes/vendor_pages.py b/app/routes/vendor_pages.py index fe01d482..0c55da9f 100644 --- a/app/routes/vendor_pages.py +++ b/app/routes/vendor_pages.py @@ -36,7 +36,9 @@ from app.api.deps import ( ) from app.services.content_page_service import content_page_service from app.services.onboarding_service import OnboardingService +from app.services.platform_settings_service import platform_settings_service from models.database.user import User +from models.database.vendor import Vendor logger = logging.getLogger(__name__) @@ -44,6 +46,79 @@ router = APIRouter() templates = Jinja2Templates(directory="app/templates") +# ============================================================================ +# HELPER: Build Vendor Dashboard Context +# ============================================================================ + + +def get_vendor_context( + request: Request, + db: Session, + current_user: User, + vendor_code: str, + **extra_context, +) -> dict: + """ + Build template context for vendor dashboard pages. + + Resolves locale/currency using the platform settings service with + vendor override support: + 1. Vendor's storefront_locale (if set) + 2. Platform's default from PlatformSettingsService + 3. Environment variable + 4. Hardcoded fallback + + Args: + request: FastAPI request object + db: Database session + current_user: Authenticated vendor user + vendor_code: Vendor subdomain/code + **extra_context: Additional variables for template + + Returns: + Dictionary with request, user, vendor, resolved locale/currency, and extra context + """ + # Load vendor from database + vendor = db.query(Vendor).filter(Vendor.subdomain == vendor_code).first() + + # Get platform defaults + platform_config = platform_settings_service.get_storefront_config(db) + + # Resolve with vendor override + storefront_locale = platform_config["locale"] + storefront_currency = platform_config["currency"] + + if vendor and vendor.storefront_locale: + storefront_locale = vendor.storefront_locale + + context = { + "request": request, + "user": current_user, + "vendor": vendor, + "vendor_code": vendor_code, + "storefront_locale": storefront_locale, + "storefront_currency": storefront_currency, + "dashboard_language": vendor.dashboard_language if vendor else "en", + } + + # Add any extra context + if extra_context: + context.update(extra_context) + + logger.debug( + "[VENDOR_CONTEXT] Context built", + extra={ + "vendor_id": vendor.id if vendor else None, + "vendor_code": vendor_code, + "storefront_locale": storefront_locale, + "storefront_currency": storefront_currency, + "extra_keys": list(extra_context.keys()) if extra_context else [], + }, + ) + + return context + + # ============================================================================ # PUBLIC ROUTES (No Authentication Required) # ============================================================================ @@ -143,11 +218,7 @@ async def vendor_onboarding_page( return templates.TemplateResponse( "vendor/onboarding.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -181,11 +252,7 @@ async def vendor_dashboard_page( return templates.TemplateResponse( "vendor/dashboard.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -201,6 +268,7 @@ async def vendor_products_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render products management page. @@ -208,11 +276,7 @@ async def vendor_products_page( """ return templates.TemplateResponse( "vendor/products.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -228,6 +292,7 @@ async def vendor_orders_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render orders management page. @@ -235,11 +300,7 @@ async def vendor_orders_page( """ return templates.TemplateResponse( "vendor/orders.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -253,6 +314,7 @@ async def vendor_order_detail_page( vendor_code: str = Path(..., description="Vendor code"), order_id: int = Path(..., description="Order ID"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render order detail page. @@ -268,12 +330,7 @@ async def vendor_order_detail_page( """ return templates.TemplateResponse( "vendor/order-detail.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - "order_id": order_id, - }, + get_vendor_context(request, db, current_user, vendor_code, order_id=order_id), ) @@ -289,6 +346,7 @@ async def vendor_customers_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render customers management page. @@ -296,11 +354,7 @@ async def vendor_customers_page( """ return templates.TemplateResponse( "vendor/customers.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -316,6 +370,7 @@ async def vendor_messages_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render messages page. @@ -323,11 +378,7 @@ async def vendor_messages_page( """ return templates.TemplateResponse( "vendor/messages.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -341,6 +392,7 @@ async def vendor_message_detail_page( vendor_code: str = Path(..., description="Vendor code"), conversation_id: int = Path(..., description="Conversation ID"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render message detail page. @@ -348,12 +400,9 @@ async def vendor_message_detail_page( """ return templates.TemplateResponse( "vendor/messages.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - "conversation_id": conversation_id, - }, + get_vendor_context( + request, db, current_user, vendor_code, conversation_id=conversation_id + ), ) @@ -369,6 +418,7 @@ async def vendor_inventory_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render inventory management page. @@ -376,11 +426,7 @@ async def vendor_inventory_page( """ return templates.TemplateResponse( "vendor/inventory.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -396,6 +442,7 @@ async def vendor_marketplace_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render marketplace import page. @@ -403,11 +450,7 @@ async def vendor_marketplace_page( """ return templates.TemplateResponse( "vendor/marketplace.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -423,6 +466,7 @@ async def vendor_letzshop_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render Letzshop integration page. @@ -430,11 +474,7 @@ async def vendor_letzshop_page( """ return templates.TemplateResponse( "vendor/letzshop.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -450,6 +490,7 @@ async def vendor_invoices_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render invoices management page. @@ -457,11 +498,7 @@ async def vendor_invoices_page( """ return templates.TemplateResponse( "vendor/invoices.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -475,6 +512,7 @@ async def vendor_team_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render team management page. @@ -482,11 +520,7 @@ async def vendor_team_page( """ return templates.TemplateResponse( "vendor/team.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -502,6 +536,7 @@ async def vendor_profile_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render vendor profile page. @@ -509,11 +544,7 @@ async def vendor_profile_page( """ return templates.TemplateResponse( "vendor/profile.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -524,6 +555,7 @@ async def vendor_settings_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render vendor settings page. @@ -531,11 +563,7 @@ async def vendor_settings_page( """ return templates.TemplateResponse( "vendor/settings.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -546,6 +574,7 @@ async def vendor_billing_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render billing and subscription management page. @@ -553,11 +582,7 @@ async def vendor_billing_page( """ return templates.TemplateResponse( "vendor/billing.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -573,6 +598,7 @@ async def vendor_notifications_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render notifications center page. @@ -580,11 +606,7 @@ async def vendor_notifications_page( """ return templates.TemplateResponse( "vendor/notifications.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -600,6 +622,7 @@ async def vendor_analytics_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render analytics and reports page. @@ -607,11 +630,7 @@ async def vendor_analytics_page( """ return templates.TemplateResponse( "vendor/analytics.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -627,6 +646,7 @@ async def vendor_content_pages_list( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render content pages management page. @@ -634,11 +654,7 @@ async def vendor_content_pages_list( """ return templates.TemplateResponse( "vendor/content-pages.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - }, + get_vendor_context(request, db, current_user, vendor_code), ) @@ -651,18 +667,14 @@ async def vendor_content_page_create( request: Request, vendor_code: str = Path(..., description="Vendor code"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render content page creation form. """ return templates.TemplateResponse( "vendor/content-page-edit.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - "page_id": None, - }, + get_vendor_context(request, db, current_user, vendor_code, page_id=None), ) @@ -676,18 +688,14 @@ async def vendor_content_page_edit( vendor_code: str = Path(..., description="Vendor code"), page_id: int = Path(..., description="Content page ID"), current_user: User = Depends(get_current_vendor_from_cookie_or_header), + db: Session = Depends(get_db), ): """ Render content page edit form. """ return templates.TemplateResponse( "vendor/content-page-edit.html", - { - "request": request, - "user": current_user, - "vendor_code": vendor_code, - "page_id": page_id, - }, + get_vendor_context(request, db, current_user, vendor_code, page_id=page_id), ) @@ -759,11 +767,22 @@ async def vendor_content_page( }, ) + # Resolve locale for shop template (uses same resolution chain as shop routes) + platform_config = platform_settings_service.get_storefront_config(db) + storefront_locale = platform_config["locale"] + storefront_currency = platform_config["currency"] + + if vendor and vendor.storefront_locale: + storefront_locale = vendor.storefront_locale + return templates.TemplateResponse( "shop/content-page.html", { "request": request, "page": page, + "vendor": vendor, "vendor_code": vendor_code, + "storefront_locale": storefront_locale, + "storefront_currency": storefront_currency, }, ) diff --git a/app/templates/shop/base.html b/app/templates/shop/base.html index 23e9e07e..3a14bb52 100644 --- a/app/templates/shop/base.html +++ b/app/templates/shop/base.html @@ -308,12 +308,12 @@ {# 1. Log Configuration (must load first) #} - {# 2. Global Shop Configuration (currency/locale settings) #} + {# 2. Global Shop Configuration (resolved via PlatformSettingsService) #} diff --git a/app/templates/vendor/base.html b/app/templates/vendor/base.html index ca5d3082..499b7e94 100644 --- a/app/templates/vendor/base.html +++ b/app/templates/vendor/base.html @@ -50,6 +50,15 @@ + + +