## API Migration (Complete)
### New Shop API Endpoints Created
- **Products API** (app/api/v1/shop/products.py)
- GET /api/v1/shop/products - Product catalog with pagination/search/filters
- GET /api/v1/shop/products/{id} - Product details
- **Cart API** (app/api/v1/shop/cart.py)
- 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 quantity
- DELETE /api/v1/shop/cart/{session_id}/items/{product_id} - Remove item
- DELETE /api/v1/shop/cart/{session_id} - Clear cart
- **Orders API** (app/api/v1/shop/orders.py)
- POST /api/v1/shop/orders - Place order (authenticated)
- GET /api/v1/shop/orders - Order history (authenticated)
- GET /api/v1/shop/orders/{id} - Order details (authenticated)
- **Auth API** (app/api/v1/shop/auth.py)
- POST /api/v1/shop/auth/register - Customer registration
- POST /api/v1/shop/auth/login - Customer login (sets cookie at path=/shop)
- POST /api/v1/shop/auth/logout - Customer logout
- POST /api/v1/shop/auth/forgot-password - Password reset request
- POST /api/v1/shop/auth/reset-password - Password reset
**Total: 18 new shop API endpoints**
### Middleware Enhancement
Updated VendorContextMiddleware (middleware/vendor_context.py):
- Added is_shop_api_request() to detect /api/v1/shop/* routes
- Added extract_vendor_from_referer() to extract vendor from Referer header
- Supports path-based: /vendors/wizamart/shop/* → wizamart
- Supports subdomain: wizamart.platform.com → wizamart
- Supports custom domain: customshop.com → customshop.com
- Modified dispatch() to handle shop API specially (no longer skips)
- Vendor context now injected into request.state.vendor for shop API calls
### Frontend Migration (Complete)
Updated all shop templates to use new API endpoints:
- app/templates/shop/account/login.html - Updated login endpoint
- app/templates/shop/account/register.html - Updated register endpoint
- app/templates/shop/product.html - Updated 4 API calls (products, cart)
- app/templates/shop/cart.html - Updated 3 API calls (get, update, delete)
- app/templates/shop/products.html - Activated product loading from API
**Total: 9 API endpoint migrations across 5 templates**
### Old Endpoint Cleanup (Complete)
Removed deprecated /api/v1/public/vendors/* shop endpoints:
- Deleted app/api/v1/public/vendors/auth.py
- Deleted app/api/v1/public/vendors/products.py
- Deleted app/api/v1/public/vendors/cart.py
- Deleted app/api/v1/public/vendors/orders.py
- Deleted app/api/v1/public/vendors/payments.py (empty)
- Deleted app/api/v1/public/vendors/search.py (empty)
- Deleted app/api/v1/public/vendors/shop.py (empty)
Updated app/api/v1/public/__init__.py to only include vendor lookup endpoints:
- GET /api/v1/public/vendors/by-code/{code}
- GET /api/v1/public/vendors/by-subdomain/{subdomain}
- GET /api/v1/public/vendors/{id}/info
**Result: Only 3 truly public endpoints remain**
### Error Page Improvements
Updated all shop error templates to use base_url:
- app/templates/shop/errors/*.html (10 files)
- Updated error_renderer.py to calculate base_url from vendor context
- Links now work correctly for path-based, subdomain, and custom domain access
### CMS Route Handler
Added catch-all CMS route to app/routes/vendor_pages.py:
- Handles /{vendor_code}/{slug} for content pages
- Uses content_page_service for two-tier lookup (vendor override → platform default)
### Template Architecture Fix
Updated app/templates/shop/base.html:
- Changed x-data to use {% block alpine_data %} for component override
- Allows pages to specify custom Alpine.js components
- Enables page-specific state while extending shared shopLayoutData()
### Documentation (Complete)
Created comprehensive documentation:
- docs/api/shop-api-reference.md - Complete API reference with examples
- docs/architecture/API_CONSOLIDATION_PROPOSAL.md - Analysis of 3 options
- docs/architecture/API_MIGRATION_STATUS.md - Migration tracking (100% complete)
- Updated docs/api/index.md - Added Shop API section
- Updated docs/frontend/shop/architecture.md - New API structure and component pattern
## Benefits Achieved
### Cleaner URLs (~40% shorter)
Before: /api/v1/public/vendors/{vendor_id}/products
After: /api/v1/shop/products
### Better Architecture
- Middleware-driven vendor context (no manual vendor_id passing)
- Proper separation of concerns (public vs shop vs vendor APIs)
- Consistent authentication pattern
- RESTful design
### Developer Experience
- No need to track vendor_id in frontend state
- Automatic vendor context from Referer header
- Simpler API calls
- Better documentation
## Testing
- Verified middleware extracts vendor from Referer correctly
- Tested all shop API endpoints with vendor context
- Confirmed products page loads and displays products
- Verified error pages show correct links
- No old API references remain in templates
Migration Status: ✅ 100% Complete (8/8 success criteria met)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
37 KiB
╔══════════════════════════════════════════════════════════════════╗ ║ 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 ═════════════════════════════════════════════════════════════════
-
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
-
Progressive Enhancement • Works without JavaScript (basic HTML) • JavaScript adds cart, search, filters • Graceful degradation for older browsers
-
API-First Data Loading • All products from REST APIs • Client-side cart management • Real-time stock updates • Search and filtering
-
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 │ │ ├── register.html │ │ ├── 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
Example: @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:
-
Custom Domain (Production) URL: https://customdomain.com/products
- Vendor has their own domain
- base_url = "/"
- Links: /products, /about, /contact
-
Subdomain (Production) URL: https://wizamart.letzshop.com/products
- Vendor uses platform subdomain
- base_url = "/"
- Links: /products, /about, /contact
-
Path-Based (Development/Testing) URL: http://localhost:8000/vendor/wizamart/products
- Vendor accessed via path prefix
- base_url = "/vendor/wizamart/"
- Links: /vendor/wizamart/products, /vendor/wizamart/about
⚠️ CRITICAL: All template links MUST use {{ base_url }} prefix
Example: ❌ BAD: Products ✅ GOOD: Products
❌ BAD: Contact ✅ GOOD: Contact
How It Works:
- VendorContextMiddleware detects access method
- Sets request.state.vendor_context with detection_method
- get_shop_context() calculates base_url from detection_method
- Templates use {{ base_url }} for all internal links
- Links work correctly regardless of access method
Layer 2: MIDDLEWARE ────────────────────────────────────────────────────────────────── Purpose: Vendor & Theme Identification
Two middleware components work together:
-
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
-
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 %}
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):
- log-config.js ← Logging system (loads first)
- icons.js ← Icon registry
- shop-layout.js ← Alpine component (before Alpine!)
- utils.js ← Utility functions
- api-client.js ← API wrapper
- Alpine.js (deferred) ← Loads last
- 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:
- Always extend shopLayoutData() in page components
- Override init() if you need page-specific initialization
- Call parent methods when needed (this.loadCart(), this.showToast())
- Keep page-specific state in the page component
- 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:
- Browser makes API call from shop page (e.g., /vendors/wizamart/shop/products)
- Browser automatically sends Referer header: http://localhost:8000/vendors/wizamart/shop/products
- VendorContextMiddleware extracts vendor from Referer header
- Middleware sets request.state.vendor = <Vendor: wizamart>
- API endpoint accesses vendor: vendor = request.state.vendor
- No vendor_id needed in URL!
🔄 DATA FLOW ═════════════════════════════════════════════════════════════════
Page Load Flow: ──────────────────────────────────────────────────────────────────
- Customer → visits acme-shop.com (or /vendors/acme/shop/products)
- Vendor Middleware → Identifies "ACME" vendor from domain/path
- Theme Middleware → Loads ACME's theme config
- FastAPI → Renders shop/products.html
- Browser → Receives HTML with theme CSS variables
- Alpine.js → init() executes
- JavaScript → GET /api/v1/shop/products (with Referer header)
- Middleware → Extracts vendor from Referer, injects into request.state
- API → Returns product list JSON for ACME vendor
- Alpine.js → Updates products array
- Browser → DOM updates with product cards
Add to Cart Flow: ──────────────────────────────────────────────────────────────────
- Customer → Clicks "Add to Cart"
- Alpine.js → addToCart(product, quantity)
- Alpine.js → Updates cart array
- Alpine.js → Saves to localStorage
- Alpine.js → Updates cartCount badge
- Alpine.js → Shows toast notification
- Browser → Cart icon updates automatically
Checkout Flow: ──────────────────────────────────────────────────────────────────
- Customer → Goes to /cart
- Page → Loads cart from localStorage
- Customer → Fills checkout form
- Alpine.js → POST /api/v1/shop/orders (with Referer header)
- Middleware → Extracts vendor from Referer
- API → Creates order + payment intent for vendor
- Alpine.js → Redirects to payment
- Payment → Completes
- Redirect → /order/{order_id}/confirmation
🎨 MULTI-THEME SYSTEM ═════════════════════════════════════════════════════════════════
How Themes Work:
-
Database Storage • Each vendor has a theme record • Stores colors, fonts, logos, layout prefs • Custom CSS per vendor
-
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; }
-
Usage in Templates Buy Now
-
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:
-
Search Modal • Overlay with input • Live search as you type • Keyboard shortcuts (Cmd+K)
-
Search API POST /api/v1/shop/{vendor_code}/search { "query": "laptop", "category": "electronics", "min_price": 100, "max_price": 1000, "sort": "price:asc" }
-
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:
Touch Optimization: • Larger touch targets (min 44x44px) • Swipeable carousels • Touch-friendly buttons • Tap to zoom images
🌙 DARK MODE ═════════════════════════════════════════════════════════════════
Implementation:
- Alpine.js state: dark: boolean
- HTML class binding: :class="{ 'dark': dark }"
- Tailwind variants: dark:bg-gray-800
- LocalStorage persistence
- Vendor colors adapt to dark mode
Toggle Button: <button @click="toggleTheme()">
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:
- Login/Register → POST /api/v1/shop/auth/login (with Referer header)
- Middleware → Extracts vendor from Referer
- API → Validates credentials for vendor's customers
- API → Returns JWT token + sets cookie (path=/shop)
- JavaScript → Store token in localStorage
- API Client → Add token to authenticated requests
- Optional → Use account features (orders, profile, etc.)
📡 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:
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:
-
Image Optimization • WebP format • Lazy loading • Responsive images (srcset) • CDN delivery
-
Code Splitting • Base layout loads first • Page-specific JS loads after • Deferred non-critical CSS
-
Caching • Browser cache for assets • LocalStorage for cart • Service worker (optional)
-
Lazy Loading • Products load as you scroll • Images load when visible • Infinite scroll for large catalogs
-
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
📊 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:
-
Understand Architecture (1 hour) → Read this document → Review file structure → Examine base template → Understand theme system
-
Study Existing Page (2 hours) → Open home.html → Open shop-layout.js → Trace product loading flow → Examine cart management
-
Create Simple Page (4 hours) → Copy templates → Modify for new feature → Test with different vendor themes → Verify responsive design
-
Add Complex Feature (1 day) → Product filters → Cart operations → API integration → Search functionality
-
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
🔒 SECURITY ═════════════════════════════════════════════════════════════════
Best Practices:
-
Input Validation ✅ Validate all form inputs ✅ Sanitize user content ✅ XSS prevention
-
Cart Security ✅ Validate cart on server ✅ Check stock availability ✅ Verify prices server-side ✅ Never trust client cart
-
Payment Security ✅ Use trusted payment providers ✅ Never store card details ✅ HTTPS only ✅ PCI compliance
-
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 ══════════════════════════════════════════════════════════════════