# app/modules/loyalty/routes/pages/store.py """ Loyalty Store Page Routes (HTML rendering). Store pages for: - Loyalty terminal (primary daily interface for staff) - Loyalty members management - Program settings - Stats dashboard Routes follow the standard store convention: /{store_code}/loyalty/... so they match the menu URLs in definition.py. """ import logging from fastapi import APIRouter, Depends, Path, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy.orm import Session from app.api.deps import get_current_store_from_cookie_or_header, get_db from app.modules.core.services.platform_settings_service import ( platform_settings_service, ) from app.modules.tenancy.models import Store, User from app.templates_config import templates logger = logging.getLogger(__name__) router = APIRouter() # Route configuration for module route discovery # No custom prefix — routes include /loyalty/ in their paths to follow # the standard store convention: /store/{store_code}/loyalty/... ROUTE_CONFIG = { "tags": ["store-loyalty"], } # ============================================================================ # HELPER: Build Store Context # ============================================================================ def get_store_context( request: Request, db: Session, current_user: User, store_code: str, **extra_context, ) -> dict: """Build template context for store loyalty pages.""" # Load store from database store = db.query(Store).filter(Store.subdomain == store_code).first() # Get platform defaults platform_config = platform_settings_service.get_storefront_config(db) # Resolve with store override storefront_locale = platform_config["locale"] storefront_currency = platform_config["currency"] if store and store.storefront_locale: storefront_locale = store.storefront_locale context = { "request": request, "user": current_user, "store": store, "store_code": store_code, "storefront_locale": storefront_locale, "storefront_currency": storefront_currency, "dashboard_language": store.dashboard_language if store else "en", } # Add any extra context if extra_context: context.update(extra_context) return context # ============================================================================ # LOYALTY ROOT (Redirect to Terminal) # ============================================================================ @router.get( "/{store_code}/loyalty", response_class=RedirectResponse, include_in_schema=False, ) async def store_loyalty_root( store_code: str = Path(..., description="Store code"), current_user: User = Depends(get_current_store_from_cookie_or_header), ): """ Redirect loyalty root to the terminal (primary daily interface). Menu item "Dashboard" points here. """ return RedirectResponse( url=f"/store/{store_code}/loyalty/terminal", status_code=302, ) # ============================================================================ # LOYALTY TERMINAL (Primary Daily Interface) # ============================================================================ @router.get( "/{store_code}/loyalty/terminal", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_terminal( request: Request, store_code: str = Path(..., description="Store code"), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty terminal page. Primary interface for staff to look up customers, award points, and process redemptions. """ return templates.TemplateResponse( "loyalty/store/terminal.html", get_store_context(request, db, current_user, store_code), ) # ============================================================================ # LOYALTY MEMBERS # ============================================================================ @router.get( "/{store_code}/loyalty/cards", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_cards( request: Request, store_code: str = Path(..., description="Store code"), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty members list page. Shows all loyalty card holders for this merchant. """ return templates.TemplateResponse( "loyalty/store/cards.html", get_store_context(request, db, current_user, store_code), ) @router.get( "/{store_code}/loyalty/cards/{card_id}", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_card_detail( request: Request, store_code: str = Path(..., description="Store code"), card_id: int = Path(..., description="Loyalty card ID"), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty card detail page. Shows card holder info, transaction history, and actions. """ return templates.TemplateResponse( "loyalty/store/card-detail.html", get_store_context(request, db, current_user, store_code, card_id=card_id), ) # ============================================================================ # STATS DASHBOARD # ============================================================================ @router.get( "/{store_code}/loyalty/stats", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_stats( request: Request, store_code: str = Path(..., description="Store code"), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty statistics dashboard. Shows store's loyalty program metrics and trends. """ return templates.TemplateResponse( "loyalty/store/stats.html", get_store_context(request, db, current_user, store_code), ) # ============================================================================ # ENROLLMENT # ============================================================================ @router.get( "/{store_code}/loyalty/enroll", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_enroll( request: Request, store_code: str = Path(..., description="Store code"), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render customer enrollment page. Staff interface for enrolling new customers into the loyalty program. """ return templates.TemplateResponse( "loyalty/store/enroll.html", get_store_context(request, db, current_user, store_code), )