feat: loyalty feature provider, admin data fixes, storefront mobile menu
Some checks failed
CI / ruff (push) Successful in 9s
CI / pytest (push) Failing after 37m24s
CI / validate (push) Failing after 22s
CI / dependency-scanning (push) Successful in 31s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

- Add LoyaltyFeatureProvider with 11 BINARY/MERCHANT features for billing
  feature gating, wired into loyalty module definition
- Fix subscription-tiers admin page showing 0 features by populating
  feature_codes from tier relationship in all admin tier endpoints
- Fix merchants admin page showing 0 stores and N/A owner by adding
  store_count and owner_email to MerchantResponse and eager-loading owner
- Add "no tiers" warning with link in subscription creation modal when
  platform has no configured tiers
- Add missing mobile menu panel to storefront base template so hamburger
  toggle actually shows navigation links

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 18:59:24 +01:00
parent 2c710ad416
commit a8b29750a5
10 changed files with 350 additions and 13 deletions

View File

@@ -67,6 +67,7 @@ def list_subscription_tiers(
for t in tiers:
resp = SubscriptionTierResponse.model_validate(t)
resp.platform_name = platforms_map.get(t.platform_id) if t.platform_id else None
resp.feature_codes = sorted(t.get_feature_codes())
tiers_response.append(resp)
return SubscriptionTierListResponse(
@@ -83,7 +84,9 @@ def get_subscription_tier(
):
"""Get a specific subscription tier by code."""
tier = admin_subscription_service.get_tier_by_code(db, tier_code)
return SubscriptionTierResponse.model_validate(tier)
resp = SubscriptionTierResponse.model_validate(tier)
resp.feature_codes = sorted(tier.get_feature_codes())
return resp
@admin_router.post("/tiers", response_model=SubscriptionTierResponse, status_code=201)
@@ -96,7 +99,9 @@ def create_subscription_tier(
tier = admin_subscription_service.create_tier(db, tier_data.model_dump())
db.commit()
db.refresh(tier)
return SubscriptionTierResponse.model_validate(tier)
resp = SubscriptionTierResponse.model_validate(tier)
resp.feature_codes = sorted(tier.get_feature_codes())
return resp
@admin_router.patch("/tiers/{tier_code}", response_model=SubscriptionTierResponse)
@@ -111,7 +116,9 @@ def update_subscription_tier(
tier = admin_subscription_service.update_tier(db, tier_code, update_data)
db.commit()
db.refresh(tier)
return SubscriptionTierResponse.model_validate(tier)
resp = SubscriptionTierResponse.model_validate(tier)
resp.feature_codes = sorted(tier.get_feature_codes())
return resp
@admin_router.delete("/tiers/{tier_code}", status_code=204)