feat(storefront): homepage, module gating, widget protocol, i18n fixes
Some checks failed
CI / ruff (push) Successful in 14s
CI / pytest (push) Failing after 2h32m45s
CI / validate (push) Successful in 30s
CI / dependency-scanning (push) Successful in 31s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

Storefront homepage & module gating:
- CMS owns storefront GET / (slug="home" with 3-tier resolution)
- Catalog loses GET / (keeps /products only)
- Store root redirect (GET / → /store/dashboard or /store/login)
- Route gating: non-core modules return 404 when disabled for platform
- Seed store default homepages per platform

Widget protocol for customer dashboard:
- StorefrontDashboardCard contract in widgets.py
- Widget aggregator get_storefront_dashboard_cards()
- Orders and Loyalty module widget providers
- Dashboard template renders contributed cards (no module names)

Landing template module-agnostic:
- CTAs driven by storefront_nav (not hardcoded module names)
- Header actions check nav item IDs (not enabled_modules)
- Remove hardcoded "Add Product" sidebar button
- Remove all enabled_modules checks from storefront templates

i18n fixes:
- Title placeholder resolution ({{store_name}}) for store default pages
- Storefront nav label_keys prefixed with module code
- Add storefront.account.* keys to 6 modules (en/fr/de/lb)
- Header/footer CMS pages use get_translated_title(current_language)
- Footer labels use i18n keys instead of hardcoded English

Icon cleanup:
- Standardize on map-pin (remove location-marker alias)
- Replace all location-marker references across templates and docs

Docs:
- Storefront builder vision proposal (6 phases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 22:53:17 +02:00
parent dd9dc04328
commit adc36246b8
58 changed files with 4691 additions and 3806 deletions

View File

@@ -9,11 +9,13 @@ Store pages for core functionality:
"""
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy.orm import Session
from app.api.deps import (
UserContext,
get_current_store_from_cookie_or_header,
get_current_store_optional,
get_db,
get_resolved_store_code,
)
@@ -24,6 +26,21 @@ from app.templates_config import templates
router = APIRouter()
# ============================================================================
# STORE ROOT REDIRECT
# ============================================================================
@router.get("/", response_class=RedirectResponse, include_in_schema=False)
async def store_root(
current_user: UserContext | None = Depends(get_current_store_optional),
):
"""Redirect /store/ based on authentication status."""
if current_user:
return RedirectResponse(url="/store/dashboard", status_code=302)
return RedirectResponse(url="/store/login", status_code=302)
# ============================================================================
# STORE DASHBOARD
# ============================================================================

View File

@@ -34,6 +34,7 @@ from sqlalchemy.orm import Session
from app.modules.contracts.widgets import (
DashboardWidget,
DashboardWidgetProviderProtocol,
StorefrontDashboardCard,
WidgetContext,
)
@@ -233,6 +234,49 @@ class WidgetAggregatorService:
return widget
return None
def get_storefront_dashboard_cards(
self,
db: Session,
store_id: int,
customer_id: int,
platform_id: int,
context: WidgetContext | None = None,
) -> list[StorefrontDashboardCard]:
"""
Get dashboard cards for the storefront customer account page.
Collects cards from all enabled modules that implement
get_storefront_dashboard_cards(), sorted by order.
Args:
db: Database session
store_id: ID of the store
customer_id: ID of the logged-in customer
platform_id: Platform ID (for module enablement check)
context: Optional filtering/scoping context
Returns:
Flat list of StorefrontDashboardCard sorted by order
"""
providers = self._get_enabled_providers(db, platform_id)
cards: list[StorefrontDashboardCard] = []
for module, provider in providers:
if not hasattr(provider, "get_storefront_dashboard_cards"):
continue
try:
module_cards = provider.get_storefront_dashboard_cards(
db, store_id, customer_id, context
)
if module_cards:
cards.extend(module_cards)
except Exception as e:
logger.warning(
f"Failed to get storefront cards from module {module.code}: {e}"
)
return sorted(cards, key=lambda c: c.order)
def get_available_categories(
self, db: Session, platform_id: int
) -> list[str]: