Files
orion/app/routes/platform_pages.py
Samir Boulahtit 0fca762b33 feat: add platform marketing homepage with signup flow
Implement complete marketing homepage for Wizamart targeting Letzshop
vendors in Luxembourg. Includes:

- Marketing homepage with hero, pricing tiers, and add-ons
- 4-step signup wizard with Stripe card collection (30-day trial)
- Letzshop vendor lookup for shop claiming
- Platform API endpoints for pricing, vendors, and signup
- Stripe SetupIntent integration for trial with card upfront
- Database fields for Letzshop vendor identity tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 10:25:36 +01:00

251 lines
7.5 KiB
Python

# app/routes/platform_pages.py
"""
Platform public page routes.
These routes serve the marketing homepage, pricing page,
Letzshop vendor finder, and signup wizard.
"""
import logging
from pathlib import Path
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from app.core.config import settings
from app.core.database import get_db
router = APIRouter()
logger = logging.getLogger(__name__)
# Get the templates directory
BASE_DIR = Path(__file__).resolve().parent.parent.parent
TEMPLATES_DIR = BASE_DIR / "app" / "templates"
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
def get_platform_context(request: Request, db: Session) -> dict:
"""Build context for platform pages."""
return {
"request": request,
"platform_name": "Wizamart",
"platform_domain": settings.platform_domain,
"stripe_publishable_key": settings.stripe_publishable_key,
"trial_days": settings.stripe_trial_days,
}
# =============================================================================
# Homepage
# =============================================================================
@router.get("/", response_class=HTMLResponse, name="platform_homepage")
async def homepage(
request: Request,
db: Session = Depends(get_db),
):
"""
Platform marketing homepage.
Displays:
- Hero section with value proposition
- Pricing tier cards
- Add-ons section
- Letzshop vendor finder
- Call to action for signup
"""
context = get_platform_context(request, db)
# Fetch tiers for display (use API service internally)
from models.database.subscription import TIER_LIMITS, TierCode
tiers = []
for tier_code, limits in TIER_LIMITS.items():
tiers.append({
"code": tier_code.value,
"name": limits["name"],
"price_monthly": limits["price_monthly_cents"] / 100,
"price_annual": (limits["price_annual_cents"] / 100) if limits.get("price_annual_cents") else None,
"orders_per_month": limits.get("orders_per_month"),
"products_limit": limits.get("products_limit"),
"team_members": limits.get("team_members"),
"features": limits.get("features", []),
"is_popular": tier_code == TierCode.PROFESSIONAL,
"is_enterprise": tier_code == TierCode.ENTERPRISE,
})
context["tiers"] = tiers
# Add-ons (hardcoded for now, will come from DB)
context["addons"] = [
{
"code": "domain",
"name": "Custom Domain",
"description": "Use your own domain (mydomain.com)",
"price": 15,
"billing_period": "year",
"icon": "globe",
},
{
"code": "ssl_premium",
"name": "Premium SSL",
"description": "EV certificate for trust badges",
"price": 49,
"billing_period": "year",
"icon": "shield-check",
},
{
"code": "email",
"name": "Email Package",
"description": "Professional email addresses",
"price": 5,
"billing_period": "month",
"icon": "mail",
"options": [
{"quantity": 5, "price": 5},
{"quantity": 10, "price": 9},
{"quantity": 25, "price": 19},
],
},
]
return templates.TemplateResponse(
"platform/homepage-wizamart.html",
context,
)
# =============================================================================
# Pricing Page
# =============================================================================
@router.get("/pricing", response_class=HTMLResponse, name="platform_pricing")
async def pricing_page(
request: Request,
db: Session = Depends(get_db),
):
"""
Standalone pricing page with detailed tier comparison.
"""
context = get_platform_context(request, db)
# Reuse tier data from homepage
from models.database.subscription import TIER_LIMITS, TierCode
tiers = []
for tier_code, limits in TIER_LIMITS.items():
tiers.append({
"code": tier_code.value,
"name": limits["name"],
"price_monthly": limits["price_monthly_cents"] / 100,
"price_annual": (limits["price_annual_cents"] / 100) if limits.get("price_annual_cents") else None,
"orders_per_month": limits.get("orders_per_month"),
"products_limit": limits.get("products_limit"),
"team_members": limits.get("team_members"),
"order_history_months": limits.get("order_history_months"),
"features": limits.get("features", []),
"is_popular": tier_code == TierCode.PROFESSIONAL,
"is_enterprise": tier_code == TierCode.ENTERPRISE,
})
context["tiers"] = tiers
context["page_title"] = "Pricing"
return templates.TemplateResponse(
"platform/pricing.html",
context,
)
# =============================================================================
# Find Your Shop (Letzshop Vendor Browser)
# =============================================================================
@router.get("/find-shop", response_class=HTMLResponse, name="platform_find_shop")
async def find_shop_page(
request: Request,
db: Session = Depends(get_db),
):
"""
Letzshop vendor browser page.
Allows vendors to search for and claim their Letzshop shop.
"""
context = get_platform_context(request, db)
context["page_title"] = "Find Your Letzshop Shop"
return templates.TemplateResponse(
"platform/find-shop.html",
context,
)
# =============================================================================
# Signup Wizard
# =============================================================================
@router.get("/signup", response_class=HTMLResponse, name="platform_signup")
async def signup_page(
request: Request,
tier: str | None = None,
annual: bool = False,
db: Session = Depends(get_db),
):
"""
Multi-step signup wizard.
Query params:
- tier: Pre-selected tier code
- annual: Pre-select annual billing
"""
context = get_platform_context(request, db)
context["page_title"] = "Start Your Free Trial"
context["selected_tier"] = tier
context["is_annual"] = annual
# Get tiers for tier selection step
from models.database.subscription import TIER_LIMITS, TierCode
tiers = []
for tier_code, limits in TIER_LIMITS.items():
tiers.append({
"code": tier_code.value,
"name": limits["name"],
"price_monthly": limits["price_monthly_cents"] / 100,
"price_annual": (limits["price_annual_cents"] / 100) if limits.get("price_annual_cents") else None,
})
context["tiers"] = tiers
return templates.TemplateResponse(
"platform/signup.html",
context,
)
@router.get("/signup/success", response_class=HTMLResponse, name="platform_signup_success")
async def signup_success_page(
request: Request,
vendor_code: str | None = None,
db: Session = Depends(get_db),
):
"""
Signup success page.
Shown after successful account creation.
"""
context = get_platform_context(request, db)
context["page_title"] = "Welcome to Wizamart!"
context["vendor_code"] = vendor_code
return templates.TemplateResponse(
"platform/signup-success.html",
context,
)