# 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 view (read-only) - Program edit (settings) - Analytics dashboard Routes follow the standard store convention: /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, get_resolved_store_code, ) 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( "/loyalty", response_class=RedirectResponse, include_in_schema=False, ) async def store_loyalty_root( store_code: str = Depends(get_resolved_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( "/loyalty/terminal", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_terminal( request: Request, store_code: str = Depends(get_resolved_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( "/loyalty/cards", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_cards( request: Request, store_code: str = Depends(get_resolved_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( "/loyalty/cards/{card_id}", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_card_detail( request: Request, store_code: str = Depends(get_resolved_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), ) # ============================================================================ # PROGRAM VIEW (Read-only) # ============================================================================ @router.get( "/loyalty/program", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_program( request: Request, store_code: str = Depends(get_resolved_store_code), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty program view page (read-only). Shows program configuration. Edit button shown to merchant owners only. """ return templates.TemplateResponse( "loyalty/store/program.html", get_store_context(request, db, current_user, store_code), ) # ============================================================================ # PROGRAM EDIT (Merchant Owner) # ============================================================================ @router.get( "/loyalty/program/edit", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_program_edit( request: Request, store_code: str = Depends(get_resolved_store_code), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty program edit page. Allows merchant owners to create or edit their loyalty program. """ return templates.TemplateResponse( "loyalty/store/settings.html", get_store_context(request, db, current_user, store_code), ) # ============================================================================ # ANALYTICS DASHBOARD # ============================================================================ @router.get( "/loyalty/analytics", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_analytics( request: Request, store_code: str = Depends(get_resolved_store_code), current_user: User = Depends(get_current_store_from_cookie_or_header), db: Session = Depends(get_db), ): """ Render loyalty analytics dashboard. Shows store's loyalty program metrics and trends. """ return templates.TemplateResponse( "loyalty/store/analytics.html", get_store_context(request, db, current_user, store_code), ) # ============================================================================ # ENROLLMENT # ============================================================================ @router.get( "/loyalty/enroll", response_class=HTMLResponse, include_in_schema=False, ) async def store_loyalty_enroll( request: Request, store_code: str = Depends(get_resolved_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), )