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 @@
+
+
+