Files
orion/docs/frontend/shop/architecture.md
Samir Boulahtit 86d67b5cfb feat: add customer authentication pages and documentation
Add complete customer authentication UI with login, registration,
forgot password, and dashboard pages.

Templates Added:
- app/templates/shop/account/login.html
  - Two-column layout with vendor branding
  - Email/password login with validation
  - Password visibility toggle
  - "Remember me" functionality
  - Error/success alerts
  - Loading states with spinner
- app/templates/shop/account/register.html
  - Customer registration form
  - Client-side validation (password strength, email format)
  - Marketing consent checkbox
  - Confirm password matching
- app/templates/shop/account/forgot-password.html
  - Password reset request page
  - Email validation
  - Success confirmation
- app/templates/shop/account/dashboard.html
  - Customer account dashboard
  - Overview of orders, profile, addresses

Styles Added:
- static/shared/css/auth.css
  - Authentication page styling
  - Two-column layout system
  - Form components and validation states
  - Theme-aware with CSS variables
  - Dark mode support
  - Mobile responsive
- static/shared/css/base.css updates
  - Enhanced utility classes
  - Additional form styles
  - Improved button states

Documentation Added:
- docs/frontend/shop/authentication-pages.md
  - Comprehensive guide to auth page implementation
  - Component architecture
  - API integration patterns
  - Theme customization
- docs/development/CUSTOMER_AUTHENTICATION_IMPLEMENTATION.md
  - Implementation details and technical decisions
  - Security considerations
  - Testing procedures
- docs/development/CUSTOMER_AUTH_SUMMARY.md
  - Quick reference guide
  - Endpoints and flows
- Updated docs/frontend/shop/architecture.md
  - Added authentication section
  - Documented all auth pages
- Updated docs/frontend/shop/page-templates.md
  - Added auth template documentation
- Updated mkdocs.yml
  - Added new documentation pages to navigation

Features:
- Full theme integration with vendor branding
- Alpine.js reactive components
- Tailwind CSS utility-first styling
- Client and server-side validation
- JWT token management
- Multi-access routing support (domain/subdomain/path)
- Error handling with user-friendly messages
- Loading states and animations
- Mobile responsive design
- Dark mode support

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 21:09:27 +01:00

1346 lines
47 KiB
Markdown

╔══════════════════════════════════════════════════════════════════╗
║ SHOP FRONTEND ARCHITECTURE OVERVIEW ║
║ Alpine.js + Jinja2 + Tailwind CSS + Multi-Theme ║
╚══════════════════════════════════════════════════════════════════╝
📦 WHAT IS THIS?
═════════════════════════════════════════════════════════════════
Customer-facing shop frontend provides visitors with a branded
e-commerce experience unique to each vendor. Built with:
✅ Jinja2 Templates (server-side rendering)
✅ Alpine.js (client-side reactivity)
✅ Tailwind CSS (utility-first styling)
✅ Multi-Theme System (vendor branding)
✅ FastAPI (backend routes)
🎯 KEY PRINCIPLES
═════════════════════════════════════════════════════════════════
1. Theme-First Design
• Each vendor has unique colors, fonts, logos
• CSS variables for dynamic theming
• Custom CSS support per vendor
• Dark mode with vendor colors
2. Progressive Enhancement
• Works without JavaScript (basic HTML)
• JavaScript adds cart, search, filters
• Graceful degradation for older browsers
3. API-First Data Loading
• All products from REST APIs
• Client-side cart management
• Real-time stock updates
• Search and filtering
4. Responsive & Mobile-First
• Mobile-first Tailwind approach
• Touch-friendly interactions
• Optimized images
• Fast page loads
📁 FILE STRUCTURE
═════════════════════════════════════════════════════════════════
app/
├── templates/shop/
│ ├── base.html ← ✅ Base template (layout + theme)
│ ├── home.html ← ✅ Homepage / featured products
│ ├── products.html ← ✅ Product catalog with filters
│ ├── product.html ← Product detail page
│ ├── cart.html ← Shopping cart
│ ├── checkout.html ← Checkout flow
│ ├── search.html ← Search results
│ ├── account/ ← Customer account pages
│ │ ├── login.html ← ✅ Customer login (IMPLEMENTED)
│ │ ├── register.html ← ✅ Customer registration (IMPLEMENTED)
│ │ ├── forgot-password.html ← ✅ Password reset (IMPLEMENTED)
│ │ ├── dashboard.html
│ │ ├── orders.html
│ │ ├── profile.html
│ │ └── addresses.html
│ └── errors/ ← Error pages
│ ├── 400.html
│ ├── 404.html
│ └── 500.html
├── static/shop/
│ ├── css/
│ │ └── shop.css ← ✅ Shop-specific styles (IMPLEMENTED)
│ ├── js/
│ │ └── shop-layout.js ← ✅ Base shop functionality (IMPLEMENTED)
│ └── img/
│ └── (placeholder images)
├── static/shared/ ← ✅ Shared across all areas (IMPLEMENTED)
│ ├── js/
│ │ ├── log-config.js ← ✅ Logging setup
│ │ ├── icons.js ← ✅ Icon registry
│ │ ├── utils.js ← ✅ Utility functions
│ │ └── api-client.js ← ✅ API wrapper
│ └── css/
│ └── (shared styles if needed)
└── routes/
└── shop_pages.py ← ✅ Route handlers (IMPLEMENTED)
🏗️ ARCHITECTURE LAYERS
═════════════════════════════════════════════════════════════════
Layer 1: Routes (FastAPI)
Layer 2: Middleware (Vendor + Theme Detection)
Layer 3: Templates (Jinja2)
Layer 4: JavaScript (Alpine.js)
Layer 5: API (REST endpoints)
Layer 6: Database
Layer 1: ROUTES (FastAPI)
──────────────────────────────────────────────────────────────────
Purpose: Vendor Detection + Template Rendering
Location: app/routes/shop_pages.py
⚠️ ROUTE REGISTRATION (main.py):
The shop router is mounted at TWO prefixes to support both access methods:
# main.py
app.include_router(shop_pages.router, prefix="/shop", ...) # Domain/subdomain
app.include_router(shop_pages.router, prefix="/vendors/{vendor_code}/shop", ...) # Path-based
This means routes defined WITHOUT /shop prefix in shop_pages.py:
@router.get("/products") → /shop/products OR /vendors/{code}/shop/products
❌ COMMON MISTAKE: Don't add /shop prefix in route definitions!
@router.get("/shop/products") ❌ WRONG - creates /shop/shop/products
@router.get("/products") ✅ CORRECT - creates /shop/products
Example Route Handler:
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
@router.get("/products", response_class=HTMLResponse, include_in_schema=False)
async def shop_products_page(request: Request):
"""
Render shop homepage / product catalog.
Vendor and theme are auto-injected by middleware.
"""
return templates.TemplateResponse(
"shop/products.html",
get_shop_context(request) # Helper function
)
Helper Function:
def get_shop_context(request: Request, **extra_context) -> dict:
"""Build template context with vendor/theme from middleware"""
vendor = getattr(request.state, 'vendor', None)
theme = getattr(request.state, 'theme', None)
clean_path = getattr(request.state, 'clean_path', request.url.path)
vendor_context = getattr(request.state, 'vendor_context', None)
# Get detection method (domain, subdomain, or path)
access_method = vendor_context.get('detection_method', 'unknown') if vendor_context else 'unknown'
# Calculate base URL for links
# - Domain/subdomain: base_url = "/"
# - Path-based: base_url = "/vendor/{vendor_code}/"
base_url = "/"
if access_method == "path" and vendor:
full_prefix = vendor_context.get('full_prefix', '/vendor/')
base_url = f"{full_prefix}{vendor.subdomain}/"
return {
"request": request,
"vendor": vendor,
"theme": theme,
"clean_path": clean_path,
"access_method": access_method,
"base_url": base_url, # ⭐ Used for all links in templates
**extra_context
}
Responsibilities:
✅ Access vendor from middleware (request.state.vendor)
✅ Access theme from middleware (request.state.theme)
✅ Calculate base_url for routing-aware links
✅ Render template with context
❌ NO database queries (data loaded client-side via API)
❌ NO business logic (handled by API endpoints)
⭐ MULTI-ACCESS ROUTING (Domain, Subdomain, Path-Based)
──────────────────────────────────────────────────────────────────
The shop frontend supports THREE access methods:
1. **Custom Domain** (Production)
URL: https://customdomain.com/shop/products
- Vendor has their own domain
- base_url = "/"
- Links: /shop/products, /shop/about, /shop/contact
2. **Subdomain** (Production)
URL: https://wizamart.letzshop.com/shop/products
- Vendor uses platform subdomain
- base_url = "/"
- Links: /shop/products, /shop/about, /shop/contact
3. **Path-Based** (Development/Testing)
URL: http://localhost:8000/vendors/wizamart/shop/products
- Vendor accessed via path prefix
- base_url = "/vendors/wizamart/"
- Links: /vendors/wizamart/shop/products, /vendors/wizamart/shop/about
⚠️ CRITICAL: All template links MUST use {{ base_url }}shop/ prefix
Example:
❌ BAD: <a href="/products">Products</a>
❌ BAD: <a href="{{ base_url }}products">Products</a>
✅ GOOD: <a href="{{ base_url }}shop/products">Products</a>
Note: The router is mounted at /shop prefix in main.py, so all links need shop/ after base_url
How It Works:
1. VendorContextMiddleware detects access method
2. Sets request.state.vendor_context with detection_method
3. get_shop_context() calculates base_url from detection_method
4. Templates use {{ base_url }} for all internal links
5. Links work correctly regardless of access method
Layer 2: MIDDLEWARE
──────────────────────────────────────────────────────────────────
Purpose: Vendor & Theme Identification
Two middleware components work together:
1. Vendor Context Middleware (middleware/vendor_context.py)
• Detects vendor from domain/subdomain/path
• Sets request.state.vendor
• Sets request.state.vendor_context (includes detection_method)
• Sets request.state.clean_path (path without vendor prefix)
• Returns 404 if vendor not found
2. Theme Context Middleware (middleware/theme_context.py)
• Loads theme for detected vendor
• Sets request.state.theme
• Falls back to default theme
Order matters:
vendor_context_middleware → theme_context_middleware
Detection Methods:
- custom_domain: Vendor has custom domain
- subdomain: Vendor uses platform subdomain
- path: Vendor accessed via /vendor/{code}/ or /vendors/{code}/
Layer 3: TEMPLATES (Jinja2)
──────────────────────────────────────────────────────────────────
Purpose: HTML Structure + Vendor Branding
Location: app/templates/shop/
Template Hierarchy:
base.html (layout + theme injection)
home.html (product grid)
partials/product-card.html (components)
Example:
{% extends "shop/base.html" %}
{% block title %}{{ vendor.name }}{% endblock %}
{% block alpine_data %}shopHome(){% endblock %}
{% block content %}
<div x-show="loading">Loading products...</div>
<div x-show="!loading" class="grid grid-cols-1 md:grid-cols-4 gap-6">
<template x-for="product in products" :key="product.id">
<!-- Product card -->
</template>
</div>
{% endblock %}
Key Features:
✅ Theme CSS variables injection
✅ Vendor logo (light/dark mode)
✅ Custom CSS from theme
✅ Social links from theme
✅ Dynamic favicon
Layer 4: JAVASCRIPT (Alpine.js)
──────────────────────────────────────────────────────────────────
Purpose: Client-Side Interactivity + Cart + Search
Location: app/static/shop/js/
⚠️ CRITICAL: JavaScript Loading Order
──────────────────────────────────────────────────────────────────
Scripts MUST load in this exact order (see base.html):
1. log-config.js ← Logging system (loads first)
2. icons.js ← Icon registry
3. shop-layout.js ← Alpine component (before Alpine!)
4. utils.js ← Utility functions
5. api-client.js ← API wrapper
6. Alpine.js (deferred) ← Loads last
7. Page-specific JS ← Optional page scripts
Why This Order Matters:
• shop-layout.js defines shopLayoutData() BEFORE Alpine initializes
• Alpine.js defers to ensure DOM is ready
• Shared utilities available to all scripts
• Icons and logging available immediately
Example from base.html:
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
<script src="{{ url_for('static', path='shop/js/shop-layout.js') }}"></script>
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
Alpine.js Component Architecture:
──────────────────────────────────────────────────────────────────
⭐ BASE COMPONENT (shop-layout.js):
Provides shared functionality for all shop pages:
function shopLayoutData() {
return {
// Theme state
dark: localStorage.getItem('shop-theme') === 'dark',
// UI state
mobileMenuOpen: false,
searchOpen: false,
cartCount: 0,
// Cart state
cart: [],
init() {
shopLog.info('Shop layout initializing...');
this.loadCart();
window.addEventListener('cart-updated', () => {
this.loadCart();
});
shopLog.info('Shop layout initialized');
},
addToCart(product, quantity = 1) {
const existingIndex = this.cart.findIndex(
item => item.id === product.id
);
if (existingIndex !== -1) {
this.cart[existingIndex].quantity += quantity;
} else {
this.cart.push({
id: product.id,
name: product.name,
price: product.price,
image: product.image,
quantity: quantity
});
}
this.saveCart();
this.showToast(`${product.name} added to cart`, 'success');
},
toggleTheme() {
this.dark = !this.dark;
localStorage.setItem('shop-theme',
this.dark ? 'dark' : 'light');
shopLog.debug('Theme toggled:', this.dark ? 'dark' : 'light');
},
showToast(message, type = 'info') {
// Toast notification implementation
}
};
}
// Make globally available
window.shopLayoutData = shopLayoutData;
⭐ PAGE-SPECIFIC COMPONENTS:
Each page extends shopLayoutData() for page-specific functionality:
// Example: products.html
document.addEventListener('alpine:init', () => {
Alpine.data('shopProducts', () => ({
...shopLayoutData(), // Extend base component
// Page-specific state
products: [],
loading: true,
filters: { search: '', category: '' },
// Override init to add page-specific initialization
async init() {
shopLog.info('Products page initializing...');
this.loadCart(); // From shopLayoutData
await this.loadProducts(); // Page-specific
},
// Page-specific methods
async loadProducts() {
const response = await fetch('/api/v1/shop/products');
const data = await response.json();
this.products = data.products;
this.loading = false;
}
}));
});
Template Usage:
──────────────────────────────────────────────────────────────────
{# In base.html - uses block to allow override #}
<html x-data="{% block alpine_data %}shopLayoutData(){% endblock %}"
x-bind:class="{ 'dark': dark }">
{# In products.html - overrides to use page-specific component #}
{% block alpine_data %}shopProducts(){% endblock %}
{# In home.html - uses default base component #}
{# No block override needed, inherits shopLayoutData() #}
⭐ COMPONENT HIERARCHY:
shopLayoutData() ← Base component (shared state & methods)
shopProducts() ← Products page (extends base + products state)
shopCart() ← Cart page (extends base + cart state)
shopCheckout() ← Checkout page (extends base + order state)
shopAccount() ← Account page (extends base + user state)
Benefits:
✅ Shared functionality (theme, cart, toasts) available on all pages
✅ Each page has its own state and methods
✅ DRY - base functionality defined once
✅ Flexible - pages can override init() or add new methods
Tradeoffs:
⚠️ One component per page (not multiple components)
⚠️ All page state is at root level
⚠️ Can't easily split page into independent sub-components
Best Practices:
1. Always extend shopLayoutData() in page components
2. Override init() if you need page-specific initialization
3. Call parent methods when needed (this.loadCart(), this.showToast())
4. Keep page-specific state in the page component
5. Keep shared state in shopLayoutData()
Responsibilities:
✅ Load products from API
✅ Manage cart in localStorage
✅ Handle search and filters
✅ Update DOM reactively
✅ Theme toggling (light/dark)
✅ Mobile menu management
✅ Toast notifications
Layer 5: API (REST)
──────────────────────────────────────────────────────────────────
Purpose: Product Data + Cart + Orders
Location: app/api/v1/shop/*.py
⭐ NEW API STRUCTURE (as of 2025-11-22):
All shop endpoints use middleware-based vendor context.
NO vendor_id or vendor_code in URLs!
Example Endpoints:
GET /api/v1/shop/products ← Product catalog
GET /api/v1/shop/products/{id} ← Product details
GET /api/v1/shop/products?search=... ← Search products
GET /api/v1/shop/cart/{session_id} ← Get cart
POST /api/v1/shop/cart/{session_id}/items ← Add to cart
PUT /api/v1/shop/cart/{session_id}/items/{product_id} ← Update item
DELETE /api/v1/shop/cart/{session_id}/items/{product_id} ← Remove item
POST /api/v1/shop/orders ← Place order (auth required)
GET /api/v1/shop/orders ← Order history (auth required)
POST /api/v1/shop/auth/login ← Customer login
POST /api/v1/shop/auth/register ← Customer registration
GET /api/v1/shop/content-pages/navigation ← CMS navigation
GET /api/v1/shop/content-pages/{slug} ← CMS page content
How Vendor Context Works:
1. Browser makes API call from shop page (e.g., /vendors/wizamart/shop/products)
2. Browser automatically sends Referer header: http://localhost:8000/vendors/wizamart/shop/products
3. VendorContextMiddleware extracts vendor from Referer header
4. Middleware sets request.state.vendor = <Vendor: wizamart>
5. API endpoint accesses vendor: vendor = request.state.vendor
6. No vendor_id needed in URL!
🔄 DATA FLOW
═════════════════════════════════════════════════════════════════
Page Load Flow:
──────────────────────────────────────────────────────────────────
1. Customer → visits acme-shop.com (or /vendors/acme/shop/products)
2. Vendor Middleware → Identifies "ACME" vendor from domain/path
3. Theme Middleware → Loads ACME's theme config
4. FastAPI → Renders shop/products.html
5. Browser → Receives HTML with theme CSS variables
6. Alpine.js → init() executes
7. JavaScript → GET /api/v1/shop/products (with Referer header)
8. Middleware → Extracts vendor from Referer, injects into request.state
9. API → Returns product list JSON for ACME vendor
10. Alpine.js → Updates products array
11. Browser → DOM updates with product cards
Add to Cart Flow:
──────────────────────────────────────────────────────────────────
1. Customer → Clicks "Add to Cart"
2. Alpine.js → addToCart(product, quantity)
3. Alpine.js → Updates cart array
4. Alpine.js → Saves to localStorage
5. Alpine.js → Updates cartCount badge
6. Alpine.js → Shows toast notification
7. Browser → Cart icon updates automatically
Checkout Flow:
──────────────────────────────────────────────────────────────────
1. Customer → Goes to /cart
2. Page → Loads cart from localStorage
3. Customer → Fills checkout form
4. Alpine.js → POST /api/v1/shop/orders (with Referer header)
5. Middleware → Extracts vendor from Referer
6. API → Creates order + payment intent for vendor
7. Alpine.js → Redirects to payment
8. Payment → Completes
9. Redirect → /order/{order_id}/confirmation
🎨 MULTI-THEME SYSTEM
═════════════════════════════════════════════════════════════════
How Themes Work:
1. Database Storage
• Each vendor has a theme record
• Stores colors, fonts, logos, layout prefs
• Custom CSS per vendor
2. CSS Variables Injection
• base.html injects variables in <style> tag
• Variables available throughout page
• Example:
:root {
--color-primary: #6366f1;
--color-secondary: #8b5cf6;
--color-accent: #ec4899;
--color-background: #ffffff;
--color-text: #1f2937;
--font-heading: Inter, sans-serif;
--font-body: Inter, sans-serif;
}
3. Usage in Templates
<button style="background-color: var(--color-primary)">
Buy Now
</button>
4. Dark Mode
• Vendor colors adjust for dark mode
• Saved in localStorage
• Applied via :class="{ 'dark': dark }"
• Uses dark: variants in Tailwind
Theme Configuration Example:
──────────────────────────────────────────────────────────────────
{
"theme_name": "modern",
"colors": {
"primary": "#6366f1",
"secondary": "#8b5cf6",
"accent": "#ec4899",
"background": "#ffffff",
"text": "#1f2937"
},
"fonts": {
"heading": "Inter, sans-serif",
"body": "Inter, sans-serif"
},
"branding": {
"logo": "/media/vendors/acme/logo.png",
"logo_dark": "/media/vendors/acme/logo-dark.png",
"favicon": "/media/vendors/acme/favicon.ico"
},
"layout": {
"header": "fixed",
"style": "grid"
},
"social_links": {
"facebook": "https://facebook.com/acme",
"instagram": "https://instagram.com/acme"
},
"custom_css": ".product-card { border-radius: 12px; }"
}
🛒 CART SYSTEM
═════════════════════════════════════════════════════════════════
Client-Side Cart Management:
Storage: localStorage
Format: JSON array of cart items
Cart Item Structure:
{
"id": "product-123",
"name": "Product Name",
"price": 29.99,
"quantity": 2,
"image": "/media/products/image.jpg",
"vendor_code": "ACME"
}
Key Functions:
• loadCart(): Load from localStorage
• saveCart(): Persist to localStorage
• addToCart(product, quantity): Add item
• updateCartItem(id, quantity): Update quantity
• removeFromCart(id): Remove item
• clearCart(): Empty cart
• cartTotal: Computed total price
Cart Persistence:
• Survives page refresh
• Shared across shop pages
• Cleared on checkout completion
• Synced across tabs (optional)
🔍 SEARCH & FILTERS
═════════════════════════════════════════════════════════════════
Search System:
1. Search Modal
• Overlay with input
• Live search as you type
• Keyboard shortcuts (Cmd+K)
2. Search API
POST /api/v1/shop/{vendor_code}/search
{
"query": "laptop",
"category": "electronics",
"min_price": 100,
"max_price": 1000,
"sort": "price:asc"
}
3. Client-Side Filtering
• Filter products array
• No API call needed for basic filters
• Use for in-memory datasets
Filter Components:
• Category dropdown
• Price range slider
• Sort options
• Availability toggle
• Brand checkboxes
📱 RESPONSIVE DESIGN
═════════════════════════════════════════════════════════════════
Breakpoints (Tailwind):
• sm: 640px (mobile landscape)
• md: 768px (tablet)
• lg: 1024px (desktop)
• xl: 1280px (large desktop)
Product Grid Responsive:
• Mobile: 1 column
• Tablet: 2 columns
• Desktop: 4 columns
Example:
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Product cards -->
</div>
Touch Optimization:
• Larger touch targets (min 44x44px)
• Swipeable carousels
• Touch-friendly buttons
• Tap to zoom images
🌙 DARK MODE
═════════════════════════════════════════════════════════════════
Implementation:
1. Alpine.js state: dark: boolean
2. HTML class binding: :class="{ 'dark': dark }"
3. Tailwind variants: dark:bg-gray-800
4. LocalStorage persistence
5. Vendor colors adapt to dark mode
Toggle Button:
<button @click="toggleTheme()">
<svg x-show="!dark"><!-- Sun icon --></svg>
<svg x-show="dark"><!-- Moon icon --></svg>
</button>
Dark Mode Colors:
• Background: dark:bg-gray-900
• Text: dark:text-gray-100
• Borders: dark:border-gray-700
• Cards: dark:bg-gray-800
Vendor Colors:
• Primary color adjusts brightness
• Maintains brand identity
• Readable in both modes
🔐 CUSTOMER AUTHENTICATION
═════════════════════════════════════════════════════════════════
Optional Auth System:
Guest Checkout:
✅ No account required
✅ Email for order updates
✅ Quick checkout
Account Features:
✅ Order history
✅ Saved addresses
✅ Wishlist
✅ Profile management
Auth Flow:
1. Login/Register → POST /api/v1/shop/auth/login (with Referer header)
2. Middleware → Extracts vendor from Referer
3. API → Validates credentials for vendor's customers
4. API → Returns JWT token + sets cookie (path=/shop)
5. JavaScript → Store token in localStorage
6. API Client → Add token to authenticated requests
7. Optional → Use account features (orders, profile, etc.)
## Authentication Pages
*Added: 2025-11-24*
All authentication pages use Tailwind CSS, Alpine.js, and theme integration
for a consistent, branded experience across all vendors.
✅ Login Page (app/templates/shop/account/login.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/login
Features:
• Two-column layout (branding + form)
• Email and password fields with validation
• Password visibility toggle
• "Remember me" checkbox
• Error and success message alerts
• Loading states with animated spinner
• Links to register and forgot password
• Theme-aware colors from CSS variables
• Dark mode support
• Mobile responsive design
Alpine.js Component:
function customerLogin() {
return {
credentials: { email: '', password: '' },
rememberMe: false,
showPassword: false,
loading: false,
errors: {},
alert: { show: false, type: 'error', message: '' },
dark: false,
async handleLogin() {
// POST /api/v1/shop/auth/login
// Store token in localStorage
// Redirect to account or return URL
}
}
}
API Endpoint:
POST /api/v1/shop/auth/login
Body: { email_or_username, password }
Returns: { access_token, user }
✅ Register Page (app/templates/shop/account/register.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/register
Features:
• Two-column layout with vendor branding
• First name, last name, email fields
• Phone number (optional)
• Password with strength requirements
• Confirm password field
• Marketing consent checkbox
• Real-time client-side validation
• Password visibility toggle
• Theme-aware styling
• Loading states
• Redirects to login after success
Validation Rules:
• First name: required
• Last name: required
• Email: required, valid format
• Password: min 8 chars, 1 letter, 1 number
• Confirm password: must match password
Alpine.js Component:
function customerRegistration() {
return {
formData: {
first_name: '', last_name: '', email: '',
phone: '', password: '', marketing_consent: false
},
confirmPassword: '',
validateForm() {
// Validates all fields
// Returns true if valid
},
async handleRegister() {
// POST /api/v1/shop/auth/register
// Redirect to login?registered=true
}
}
}
API Endpoint:
POST /api/v1/shop/auth/register
Body: { first_name, last_name, email, phone?, password, marketing_consent }
Returns: { message }
✅ Forgot Password Page (app/templates/shop/account/forgot-password.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/forgot-password
Features:
• Two-column layout with vendor branding
• Email input field
• Two-state interface:
1. Form submission state
2. Success confirmation state
• Success state with checkmark icon
• Option to retry if email not received
• Theme-aware styling
• Links back to login and shop
• Dark mode support
Alpine.js Component:
function forgotPassword() {
return {
email: '',
emailSent: false,
loading: false,
async handleSubmit() {
// POST /api/v1/shop/auth/forgot-password
// Show success message
// emailSent = true
}
}
}
API Endpoint:
POST /api/v1/shop/auth/forgot-password
Body: { email }
Returns: { message }
🎨 THEME INTEGRATION
──────────────────────────────────────────────────────────────────
All authentication pages inject vendor theme CSS variables:
<style id="vendor-theme-variables">
:root {
{% for key, value in theme.css_variables.items() %}
{{ key }}: {{ value }};
{% endfor %}
}
/* Theme-aware button and focus colors */
.btn-primary-theme {
background-color: var(--color-primary);
}
.btn-primary-theme:hover:not(:disabled) {
background-color: var(--color-primary-dark, var(--color-primary));
filter: brightness(0.9);
}
.focus-primary:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb, 124, 58, 237), 0.1);
}
</style>
Key Theme Elements:
• Left panel background: var(--color-primary)
• Submit buttons: var(--color-primary)
• Links: var(--color-primary)
• Checkboxes: var(--color-primary)
• Focus states: var(--color-primary) with transparency
• Vendor logo from theme.branding.logo
Benefits:
✅ Each vendor's auth pages match their brand
✅ Consistent with main shop design
✅ Dark mode adapts to vendor colors
✅ Professional, polished appearance
📱 RESPONSIVE DESIGN
──────────────────────────────────────────────────────────────────
Mobile (<640px):
• Vertical layout (image on top, form below)
• Smaller padding and spacing
• Full-width buttons
• Touch-friendly input fields
Tablet (640px-1024px):
• Side-by-side layout begins
• Balanced column widths
• Comfortable spacing
Desktop (>1024px):
• Full two-column layout
• Max width container (max-w-4xl)
• Centered on page
• Larger brand imagery
🔒 SECURITY FEATURES
──────────────────────────────────────────────────────────────────
Client-Side:
• Input validation before submission
• Password visibility toggle
• HTTPS required
• No sensitive data in URLs
• Token stored in localStorage (not cookies)
Server-Side (API handles):
• Password hashing (bcrypt)
• Email verification
• Rate limiting
• CSRF protection
• SQL injection prevention
📡 API CLIENT
═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/api-client.js
⭐ NEW USAGE (as of 2025-11-22):
No vendor_code needed! Vendor extracted from Referer header automatically.
Usage:
// Product catalog
const products = await fetch('/api/v1/shop/products');
// Add to cart
const response = await fetch('/api/v1/shop/cart/session123/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ product_id: 1, quantity: 2 })
});
// Place order
const order = await fetch('/api/v1/shop/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData)
});
Features:
✅ Automatic error handling
✅ JSON parsing
✅ Loading states
✅ Auth token injection (if logged in)
🐛 LOGGING
═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/log-config.js
Shop-Specific Logging:
shopLog.info('Product added to cart', product);
shopLog.error('Checkout failed', error);
shopLog.debug('Search query', { query, results });
Levels:
• INFO: User actions
• ERROR: Failures and exceptions
• DEBUG: Development debugging
• WARN: Warnings
🎭 ICONS
═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/icons.js
Usage:
<span x-html="$icon('shopping-cart', 'w-5 h-5')"></span>
<span x-html="$icon('search', 'w-6 h-6 text-primary')"></span>
Shop Icons:
• shopping-cart, shopping-bag
• heart (wishlist)
• search, filter
• eye (view)
• star (rating)
• truck (shipping)
• check (success)
• x (close)
• chevron-left, chevron-right
• spinner (loading)
🚀 PERFORMANCE
═════════════════════════════════════════════════════════════════
Optimization Techniques:
1. Image Optimization
• WebP format
• Lazy loading
• Responsive images (srcset)
• CDN delivery
2. Code Splitting
• Base layout loads first
• Page-specific JS loads after
• Deferred non-critical CSS
3. Caching
• Browser cache for assets
• LocalStorage for cart
• Service worker (optional)
4. Lazy Loading
• Products load as you scroll
• Images load when visible
• Infinite scroll for large catalogs
5. CDN Assets with Fallback
• Tailwind CSS from CDN (fallback to local)
• Alpine.js from CDN (fallback to local)
• Works offline and in restricted networks
• See: [CDN Fallback Strategy](../cdn-fallback-strategy.md)
📊 PAGE-BY-PAGE BREAKDOWN
═════════════════════════════════════════════════════════════════
/ (Homepage)
──────────────────────────────────────────────────────────────────
Purpose: Display featured products, hero section
Components:
• Hero banner with CTA
• Featured products grid
• Category cards
• About vendor section
Data Sources:
• GET /api/v1/shop/products?is_featured=true
/products
──────────────────────────────────────────────────────────────────
Purpose: Browse all products with filters
Components:
• Product grid
• Filter sidebar
• Sort dropdown
• Pagination
Data Sources:
• GET /api/v1/shop/products?skip=0&limit=20
• GET /api/v1/shop/products?search=query
• Filters applied client-side or server-side
/products/{product_id}
──────────────────────────────────────────────────────────────────
Purpose: Single product detail page
Components:
• Image gallery
• Product info
• Add to cart form
• Related products
• Reviews (optional)
Data Sources:
• GET /api/v1/shop/products/{id}
• GET /api/v1/shop/products?limit=4 (related products)
/cart
──────────────────────────────────────────────────────────────────
Purpose: Review cart contents before checkout
Components:
• Cart items list
• Quantity adjusters
• Remove buttons
• Cart total
• Checkout button
Data Sources:
• LocalStorage cart
• No API call needed
/checkout
──────────────────────────────────────────────────────────────────
Purpose: Complete purchase
Components:
• Shipping form
• Payment form
• Order summary
• Submit button
Data Sources:
• POST /api/v1/shop/orders
• Stripe/PayPal integration
/search
──────────────────────────────────────────────────────────────────
Purpose: Search results page
Components:
• Search input
• Results grid
• Filter options
• Sort options
Data Sources:
• GET /api/v1/shop/products?search=query
/category/{category_slug}
──────────────────────────────────────────────────────────────────
Purpose: Browse products by category
Components:
• Breadcrumbs
• Product grid
• Subcategories
• Filters
Data Sources:
• GET /api/v1/shop/products?category={slug}
/about
──────────────────────────────────────────────────────────────────
Purpose: About the vendor
Components:
• Vendor story
• Team photos
• Values/mission
• Contact info
Data Sources:
• Vendor info from middleware
• Static content
/contact
──────────────────────────────────────────────────────────────────
Purpose: Contact form
Components:
• Contact form
• Map (optional)
• Business hours
• Social links
Data Sources:
• CMS content page (GET /api/v1/shop/content-pages/contact)
• Form submission to vendor email
🎓 LEARNING PATH
═════════════════════════════════════════════════════════════════
For New Developers:
1. Understand Architecture (1 hour)
→ Read this document
→ Review file structure
→ Examine base template
→ Understand theme system
2. Study Existing Page (2 hours)
→ Open home.html
→ Open shop-layout.js
→ Trace product loading flow
→ Examine cart management
3. Create Simple Page (4 hours)
→ Copy templates
→ Modify for new feature
→ Test with different vendor themes
→ Verify responsive design
4. Add Complex Feature (1 day)
→ Product filters
→ Cart operations
→ API integration
→ Search functionality
5. Master Patterns (1 week)
→ All common patterns
→ Theme customization
→ Performance optimization
→ Mobile responsiveness
🔄 DEPLOYMENT CHECKLIST
═════════════════════════════════════════════════════════════════
Before Deploying:
□ Build Tailwind CSS
□ Minify JavaScript
□ Test all routes
□ Test with multiple vendor themes
□ Verify cart persistence
□ Check mobile responsive
□ Test dark mode
□ Validate product display
□ Test checkout flow
□ Check image optimization
□ Test search functionality
□ Verify social links
□ Test across browsers
□ Check console for errors
□ Test in production mode
🚨 ERROR HANDLING & ERROR PAGES
═════════════════════════════════════════════════════════════════
Multi-Access Aware Error Pages:
All shop error pages (404, 500, etc.) are vendor-context aware and display
correct links based on the access method (domain, subdomain, or path-based).
Error Page Templates:
• app/templates/shop/errors/404.html - Not Found
• app/templates/shop/errors/400.html - Bad Request
• app/templates/shop/errors/401.html - Unauthorized
• app/templates/shop/errors/403.html - Forbidden
• app/templates/shop/errors/422.html - Validation Error
• app/templates/shop/errors/429.html - Rate Limited
• app/templates/shop/errors/500.html - Server Error
• app/templates/shop/errors/502.html - Bad Gateway
• app/templates/shop/errors/base.html - Base error template
• app/templates/shop/errors/generic.html - Generic error
Error Renderer (app/exceptions/error_renderer.py):
Calculates base_url dynamically based on vendor access method:
def _get_context_data(self, request: Request, ...):
vendor = getattr(request.state, 'vendor', None)
access_method = getattr(request.state, "access_method", None)
vendor_context = getattr(request.state, "vendor_context", None)
# Calculate base_url for shop links
base_url = "/"
if access_method == "path" and vendor:
full_prefix = vendor_context.get('full_prefix', '/vendor/')
base_url = f"{full_prefix}{vendor.subdomain}/"
return {
"request": request,
"vendor": vendor,
"base_url": base_url, # ⭐ Used in error templates
...
}
Error Template Usage:
All error page links use {{ base_url }} prefix for correct routing:
{# ❌ BAD - Hardcoded links #}
<a href="/">Continue Shopping</a>
<a href="/products">View Products</a>
{# ✅ GOOD - Context-aware links #}
<a href="{{ base_url or '/' }}">Continue Shopping</a>
<a href="{{ base_url or '/' }}products">View Products</a>
How It Works:
1. Error occurs (404, 500, etc.)
2. Exception handler detects shop context
3. error_renderer.py calculates base_url from vendor_context
4. Error template renders with correct base_url
5. Links work for all access methods:
- Domain: customshop.com → base_url = "/"
- Subdomain: wizamart.platform.com → base_url = "/"
- Path: localhost/vendors/wizamart/ → base_url = "/vendors/wizamart/"
Benefits:
✅ Error pages work correctly regardless of access method
✅ No broken links in error states
✅ Consistent user experience
✅ Vendor branding maintained in errors
🔒 SECURITY
═════════════════════════════════════════════════════════════════
Best Practices:
1. Input Validation
✅ Validate all form inputs
✅ Sanitize user content
✅ XSS prevention
2. Cart Security
✅ Validate cart on server
✅ Check stock availability
✅ Verify prices server-side
✅ Never trust client cart
3. Payment Security
✅ Use trusted payment providers
✅ Never store card details
✅ HTTPS only
✅ PCI compliance
4. Rate Limiting
✅ Search endpoint throttling
✅ Contact form limits
✅ Checkout attempt limits
📚 REFERENCE LINKS
═════════════════════════════════════════════════════════════════
Documentation:
• Alpine.js: https://alpinejs.dev/
• Tailwind CSS: https://tailwindcss.com/
• Jinja2: https://jinja.palletsprojects.com/
• FastAPI: https://fastapi.tiangolo.com/
Internal Docs:
• Page Template Guide: FRONTEND_SHOP_ALPINE_PAGE_TEMPLATE.md
• Multi-Theme Guide: MULTI_THEME_SHOP_GUIDE.md
• API Documentation: API_REFERENCE.md
• Database Schema: DATABASE_SCHEMA.md
══════════════════════════════════════════════════════════════════
SHOP FRONTEND ARCHITECTURE
Theme-Driven, Customer-Focused, Brand-Consistent
══════════════════════════════════════════════════════════════════