feat(storefront): homepage, module gating, widget protocol, i18n fixes
Some checks failed
Some checks failed
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:
@@ -58,6 +58,13 @@ def _get_feature_provider():
|
||||
return loyalty_feature_provider
|
||||
|
||||
|
||||
def _get_widget_provider():
|
||||
"""Lazy import of widget provider to avoid circular imports."""
|
||||
from app.modules.loyalty.services.loyalty_widgets import loyalty_widget_provider
|
||||
|
||||
return loyalty_widget_provider
|
||||
|
||||
|
||||
def _get_onboarding_provider():
|
||||
"""Lazy import of onboarding provider to avoid circular imports."""
|
||||
from app.modules.loyalty.services.loyalty_onboarding_service import (
|
||||
@@ -289,7 +296,7 @@ loyalty_module = ModuleDefinition(
|
||||
items=[
|
||||
MenuItemDefinition(
|
||||
id="loyalty",
|
||||
label_key="storefront.account.loyalty",
|
||||
label_key="loyalty.storefront.account.loyalty",
|
||||
icon="gift",
|
||||
route="account/loyalty",
|
||||
order=60,
|
||||
@@ -328,6 +335,8 @@ loyalty_module = ModuleDefinition(
|
||||
],
|
||||
# Feature provider for billing feature gating
|
||||
feature_provider=_get_feature_provider,
|
||||
# Widget provider for storefront dashboard cards
|
||||
widget_provider=_get_widget_provider,
|
||||
# Onboarding provider for post-signup checklist
|
||||
onboarding_provider=_get_onboarding_provider,
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
81
app/modules/loyalty/services/loyalty_widgets.py
Normal file
81
app/modules/loyalty/services/loyalty_widgets.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# app/modules/loyalty/services/loyalty_widgets.py
|
||||
"""
|
||||
Loyalty dashboard widget provider.
|
||||
|
||||
Provides storefront dashboard cards for loyalty-related data.
|
||||
Implements get_storefront_dashboard_cards from DashboardWidgetProviderProtocol.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.contracts.widgets import (
|
||||
DashboardWidget,
|
||||
StorefrontDashboardCard,
|
||||
WidgetContext,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoyaltyWidgetProvider:
|
||||
"""Widget provider for loyalty module."""
|
||||
|
||||
@property
|
||||
def widgets_category(self) -> str:
|
||||
return "loyalty"
|
||||
|
||||
def get_store_widgets(
|
||||
self,
|
||||
db: Session,
|
||||
store_id: int,
|
||||
context: WidgetContext | None = None,
|
||||
) -> list[DashboardWidget]:
|
||||
return []
|
||||
|
||||
def get_platform_widgets(
|
||||
self,
|
||||
db: Session,
|
||||
platform_id: int,
|
||||
context: WidgetContext | None = None,
|
||||
) -> list[DashboardWidget]:
|
||||
return []
|
||||
|
||||
def get_storefront_dashboard_cards(
|
||||
self,
|
||||
db: Session,
|
||||
store_id: int,
|
||||
customer_id: int,
|
||||
context: WidgetContext | None = None,
|
||||
) -> list[StorefrontDashboardCard]:
|
||||
"""Provide the Loyalty Rewards card for the customer dashboard."""
|
||||
from app.modules.loyalty.models.loyalty_card import LoyaltyCard
|
||||
|
||||
card = (
|
||||
db.query(LoyaltyCard)
|
||||
.filter(
|
||||
LoyaltyCard.customer_id == customer_id,
|
||||
LoyaltyCard.is_active.is_(True),
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
points = card.points_balance if card else None
|
||||
subtitle = "View your points & rewards" if card else "Join our rewards program"
|
||||
|
||||
return [
|
||||
StorefrontDashboardCard(
|
||||
key="loyalty.rewards",
|
||||
icon="gift",
|
||||
title="Loyalty Rewards",
|
||||
subtitle=subtitle,
|
||||
route="account/loyalty",
|
||||
value=points,
|
||||
value_label="Points Balance" if points is not None else None,
|
||||
order=30,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
loyalty_widget_provider = LoyaltyWidgetProvider()
|
||||
Reference in New Issue
Block a user