docs: add consolidated dev URL reference and migrate /shop to /storefront
Some checks failed
CI / ruff (push) Successful in 10s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

- Add Development URL Quick Reference section to url-routing overview
  with all login URLs, entry points, and full examples
- Replace /shop/ path segments with /storefront/ across 50 docs files
- Update file references: shop_pages.py → storefront_pages.py,
  templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/
- Preserve domain references (orion.shop) and /store/ staff dashboard paths
- Archive docs left unchanged (historical)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 13:23:44 +01:00
parent 3df75e2e78
commit d648c921b7
50 changed files with 1104 additions and 1049 deletions

View File

@@ -1,12 +1,12 @@
╔══════════════════════════════════════════════════════════════════╗
║ SHOP FRONTEND ARCHITECTURE OVERVIEW ║
║ STOREFRONT FRONTEND ARCHITECTURE OVERVIEW ║
║ Alpine.js + Jinja2 + Tailwind CSS + Multi-Theme ║
╚══════════════════════════════════════════════════════════════════╝
📦 WHAT IS THIS?
═════════════════════════════════════════════════════════════════
Customer-facing shop frontend provides visitors with a branded
Customer-facing storefront frontend provides visitors with a branded
e-commerce experience unique to each store. Built with:
✅ Jinja2 Templates (server-side rendering)
✅ Alpine.js (client-side reactivity)
@@ -46,7 +46,7 @@ e-commerce experience unique to each store. Built with:
═════════════════════════════════════════════════════════════════
app/
├── templates/shop/
├── templates/storefront/
│ ├── base.html ← ✅ Base template (layout + theme)
│ ├── home.html ← ✅ Homepage / featured products
│ ├── products.html ← ✅ Product catalog with filters
@@ -67,11 +67,11 @@ app/
│ ├── 404.html
│ └── 500.html
├── static/shop/
├── static/storefront/
│ ├── css/
│ │ └── shop.css ← ✅ Shop-specific styles (IMPLEMENTED)
│ │ └── storefront.css ← ✅ Storefront-specific styles (IMPLEMENTED)
│ ├── js/
│ │ └── shop-layout.js ← ✅ Base shop functionality (IMPLEMENTED)
│ │ └── storefront-layout.js ← ✅ Base storefront functionality (IMPLEMENTED)
│ └── img/
│ └── (placeholder images)
@@ -85,7 +85,7 @@ app/
│ └── (shared styles if needed)
└── routes/
└── shop_pages.py ← ✅ Route handlers (IMPLEMENTED)
└── storefront_pages.py ← ✅ Route handlers (IMPLEMENTED)
🏗️ ARCHITECTURE LAYERS
@@ -107,37 +107,37 @@ Layer 6: Database
Layer 1: ROUTES (FastAPI)
──────────────────────────────────────────────────────────────────
Purpose: Store Detection + Template Rendering
Location: app/routes/shop_pages.py
Location: app/routes/storefront_pages.py
⚠️ ROUTE REGISTRATION (main.py):
The shop router is mounted at TWO prefixes to support both access methods:
The storefront 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="/stores/{store_code}/shop", ...) # Path-based
app.include_router(storefront_pages.router, prefix="/storefront", ...) # Domain/subdomain
app.include_router(storefront_pages.router, prefix="/storefront/{store_code}", ...) # Path-based
This means routes defined WITHOUT /shop prefix in shop_pages.py:
@router.get("/products") → /shop/products OR /stores/{code}/shop/products
This means routes defined WITHOUT /storefront prefix in storefront_pages.py:
@router.get("/products") → /storefront/products OR /storefront/{code}/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
❌ COMMON MISTAKE: Don't add /storefront prefix in route definitions!
@router.get("/storefront/products") ❌ WRONG - creates /storefront/storefront/products
@router.get("/products") ✅ CORRECT - creates /storefront/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):
async def storefront_products_page(request: Request):
"""
Render shop homepage / product catalog.
Render storefront homepage / product catalog.
Store and theme are auto-injected by middleware.
"""
return templates.TemplateResponse(
"shop/products.html",
get_shop_context(request) # Helper function
"storefront/products.html",
get_storefront_context(request) # Helper function
)
Helper Function:
def get_shop_context(request: Request, **extra_context) -> dict:
def get_storefront_context(request: Request, **extra_context) -> dict:
"""Build template context with store/theme from middleware"""
store = getattr(request.state, 'store', None)
theme = getattr(request.state, 'theme', None)
@@ -176,39 +176,39 @@ Responsibilities:
⭐ MULTI-ACCESS ROUTING (Domain, Subdomain, Path-Based)
──────────────────────────────────────────────────────────────────
The shop frontend supports THREE access methods:
The storefront frontend supports THREE access methods:
1. **Custom Domain** (Production)
URL: https://customdomain.com/shop/products
URL: https://customdomain.com/storefront/products
- Store has their own domain
- base_url = "/"
- Links: /shop/products, /shop/about, /shop/contact
- Links: /storefront/products, /storefront/about, /storefront/contact
2. **Subdomain** (Production)
URL: https://orion.letzshop.com/shop/products
URL: https://orion.letzshop.com/storefront/products
- Store uses platform subdomain
- base_url = "/"
- Links: /shop/products, /shop/about, /shop/contact
- Links: /storefront/products, /storefront/about, /storefront/contact
3. **Path-Based** (Development/Testing)
URL: http://localhost:8000/stores/orion/shop/products
URL: http://localhost:8000/storefront/orion/products
- Store accessed via path prefix
- base_url = "/stores/orion/"
- Links: /stores/orion/shop/products, /stores/orion/shop/about
- base_url = "/storefront/orion/"
- Links: /storefront/orion/products, /storefront/orion/about
⚠️ CRITICAL: All template links MUST use {{ base_url }}shop/ prefix
⚠️ CRITICAL: All template links MUST use {{ base_url }}storefront/ 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>
✅ GOOD: <a href="{{ base_url }}storefront/products">Products</a>
Note: The router is mounted at /shop prefix in main.py, so all links need shop/ after base_url
Note: The router is mounted at /storefront prefix in main.py, so all links need storefront/ after base_url
How It Works:
1. StoreContextMiddleware detects access method
2. Sets request.state.store_context with detection_method
3. get_shop_context() calculates base_url from detection_method
3. get_storefront_context() calculates base_url from detection_method
4. Templates use {{ base_url }} for all internal links
5. Links work correctly regardless of access method
@@ -243,7 +243,7 @@ Detection Methods:
Layer 3: TEMPLATES (Jinja2)
──────────────────────────────────────────────────────────────────
Purpose: HTML Structure + Store Branding
Location: app/templates/shop/
Location: app/templates/storefront/
Template Hierarchy:
base.html (layout + theme injection)
@@ -253,7 +253,7 @@ Template Hierarchy:
partials/product-card.html (components)
Example:
{% extends "shop/base.html" %}
{% extends "storefront/base.html" %}
{% block title %}{{ store.name }}{% endblock %}
{% block alpine_data %}shopHome(){% endblock %}
{% block content %}
@@ -276,7 +276,7 @@ Key Features:
Layer 4: JAVASCRIPT (Alpine.js)
──────────────────────────────────────────────────────────────────
Purpose: Client-Side Interactivity + Cart + Search
Location: app/static/shop/js/
Location: app/static/storefront/js/
⚠️ CRITICAL: JavaScript Loading Order
──────────────────────────────────────────────────────────────────
@@ -284,14 +284,14 @@ 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!)
3. storefront-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 storefrontLayoutData() BEFORE Alpine initializes
• storefront-layout.js defines storefrontLayoutData() BEFORE Alpine initializes
• Alpine.js defers to ensure DOM is ready
• Shared utilities available to all scripts
• Icons and logging available immediately
@@ -299,7 +299,7 @@ Why This Order Matters:
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='storefront/js/storefront-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>
@@ -307,14 +307,14 @@ Example from base.html:
Alpine.js Component Architecture:
──────────────────────────────────────────────────────────────────
⭐ BASE COMPONENT (shop-layout.js):
⭐ BASE COMPONENT (storefront-layout.js):
Provides shared functionality for all shop pages:
Provides shared functionality for all storefront pages:
function storefrontLayoutData() {
return {
// Theme state
dark: localStorage.getItem('shop-theme') === 'dark',
dark: localStorage.getItem('storefront-theme') === 'dark',
// UI state
mobileMenuOpen: false,
@@ -325,12 +325,12 @@ Provides shared functionality for all shop pages:
cart: [],
init() {
shopLog.info('Shop layout initializing...');
shopLog.info('Storefront layout initializing...');
this.loadCart();
window.addEventListener('cart-updated', () => {
this.loadCart();
});
shopLog.info('Shop layout initialized');
shopLog.info('Storefront layout initialized');
},
addToCart(product, quantity = 1) {
@@ -356,7 +356,7 @@ Provides shared functionality for all shop pages:
toggleTheme() {
this.dark = !this.dark;
localStorage.setItem('shop-theme',
localStorage.setItem('storefront-theme',
this.dark ? 'dark' : 'light');
shopLog.debug('Theme toggled:', this.dark ? 'dark' : 'light');
},
@@ -393,7 +393,7 @@ Each page extends storefrontLayoutData() for page-specific functionality:
// Page-specific methods
async loadProducts() {
const response = await fetch('/api/v1/shop/products');
const response = await fetch('/api/v1/storefront/products');
const data = await response.json();
this.products = data.products;
this.loading = false;
@@ -454,30 +454,30 @@ Responsibilities:
Layer 5: API (REST)
──────────────────────────────────────────────────────────────────
Purpose: Product Data + Cart + Orders
Location: app/api/v1/shop/*.py
Location: app/api/v1/storefront/*.py
⭐ NEW API STRUCTURE (as of 2025-11-22):
All shop endpoints use middleware-based store context.
All storefront endpoints use middleware-based store context.
NO store_id or store_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
GET /api/v1/storefront/products ← Product catalog
GET /api/v1/storefront/products/{id} ← Product details
GET /api/v1/storefront/products?search=... ← Search products
GET /api/v1/storefront/cart/{session_id} ← Get cart
POST /api/v1/storefront/cart/{session_id}/items ← Add to cart
PUT /api/v1/storefront/cart/{session_id}/items/{product_id} ← Update item
DELETE /api/v1/storefront/cart/{session_id}/items/{product_id} ← Remove item
POST /api/v1/storefront/orders ← Place order (auth required)
GET /api/v1/storefront/orders ← Order history (auth required)
POST /api/v1/storefront/auth/login ← Customer login
POST /api/v1/storefront/auth/register ← Customer registration
GET /api/v1/storefront/content-pages/navigation ← CMS navigation
GET /api/v1/storefront/content-pages/{slug} ← CMS page content
How Store Context Works:
1. Browser makes API call from shop page (e.g., /stores/orion/shop/products)
2. Browser automatically sends Referer header: http://localhost:8000/stores/orion/shop/products
1. Browser makes API call from storefront page (e.g., /storefront/orion/products)
2. Browser automatically sends Referer header: http://localhost:8000/storefront/orion/products
3. StoreContextMiddleware extracts store from Referer header
4. Middleware sets request.state.store = <Store: orion>
5. API endpoint accesses store: store = request.state.store
@@ -489,13 +489,13 @@ How Store Context Works:
Page Load Flow:
──────────────────────────────────────────────────────────────────
1. Customer → visits acme-shop.com (or /stores/acme/shop/products)
1. Customer → visits acme-shop.com (or /storefront/acme/products)
2. Store Middleware → Identifies "ACME" store from domain/path
3. Theme Middleware → Loads ACME's theme config
4. FastAPI → Renders shop/products.html
4. FastAPI → Renders storefront/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)
7. JavaScript → GET /api/v1/storefront/products (with Referer header)
8. Middleware → Extracts store from Referer, injects into request.state
9. API → Returns product list JSON for ACME store
10. Alpine.js → Updates products array
@@ -516,7 +516,7 @@ 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)
4. Alpine.js → POST /api/v1/storefront/orders (with Referer header)
5. Middleware → Extracts store from Referer
6. API → Creates order + payment intent for store
7. Alpine.js → Redirects to payment
@@ -621,7 +621,7 @@ Key Functions:
Cart Persistence:
• Survives page refresh
• Shared across shop pages
• Shared across storefront pages
• Cleared on checkout completion
• Synced across tabs (optional)
@@ -637,7 +637,7 @@ Search System:
• Keyboard shortcuts (Cmd+K)
2. Search API
POST /api/v1/shop/{store_code}/search
POST /api/v1/storefront/{store_code}/search
{
"query": "laptop",
"category": "electronics",
@@ -730,10 +730,10 @@ Account Features:
✅ Profile management
Auth Flow:
1. Login/Register → POST /api/v1/shop/auth/login (with Referer header)
1. Login/Register → POST /api/v1/storefront/auth/login (with Referer header)
2. Middleware → Extracts store from Referer
3. API → Validates credentials for store's customers
4. API → Returns JWT token + sets cookie (path=/shop)
4. API → Returns JWT token + sets cookie (path=/storefront)
5. JavaScript → Store token in localStorage
6. API Client → Add token to authenticated requests
7. Optional → Use account features (orders, profile, etc.)
@@ -745,9 +745,9 @@ Auth Flow:
All authentication pages use Tailwind CSS, Alpine.js, and theme integration
for a consistent, branded experience across all stores.
✅ Login Page (app/templates/shop/account/login.html)
✅ Login Page (app/templates/storefront/account/login.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/login
Route: /storefront/account/login
Features:
• Two-column layout (branding + form)
@@ -773,7 +773,7 @@ Alpine.js Component:
dark: false,
async handleLogin() {
// POST /api/v1/shop/auth/login
// POST /api/v1/storefront/auth/login
// Store token in localStorage
// Redirect to account or return URL
}
@@ -781,13 +781,13 @@ Alpine.js Component:
}
API Endpoint:
POST /api/v1/shop/auth/login
POST /api/v1/storefront/auth/login
Body: { email_or_username, password }
Returns: { access_token, user }
✅ Register Page (app/templates/shop/account/register.html)
✅ Register Page (app/templates/storefront/account/register.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/register
Route: /storefront/account/register
Features:
• Two-column layout with store branding
@@ -824,20 +824,20 @@ Alpine.js Component:
},
async handleRegister() {
// POST /api/v1/shop/auth/register
// POST /api/v1/storefront/auth/register
// Redirect to login?registered=true
}
}
}
API Endpoint:
POST /api/v1/shop/auth/register
POST /api/v1/storefront/auth/register
Body: { first_name, last_name, email, phone?, password, marketing_consent }
Returns: { message }
✅ Forgot Password Page (app/templates/shop/account/forgot-password.html)
✅ Forgot Password Page (app/templates/storefront/account/forgot-password.html)
──────────────────────────────────────────────────────────────────
Route: /shop/account/forgot-password
Route: /storefront/account/forgot-password
Features:
• Two-column layout with store branding
@@ -848,7 +848,7 @@ Features:
• Success state with checkmark icon
• Option to retry if email not received
• Theme-aware styling
• Links back to login and shop
• Links back to login and storefront
• Dark mode support
Alpine.js Component:
@@ -859,7 +859,7 @@ Alpine.js Component:
loading: false,
async handleSubmit() {
// POST /api/v1/shop/auth/forgot-password
// POST /api/v1/storefront/auth/forgot-password
// Show success message
// emailSent = true
}
@@ -867,7 +867,7 @@ Alpine.js Component:
}
API Endpoint:
POST /api/v1/shop/auth/forgot-password
POST /api/v1/storefront/auth/forgot-password
Body: { email }
Returns: { message }
@@ -907,7 +907,7 @@ Key Theme Elements:
Benefits:
✅ Each store's auth pages match their brand
✅ Consistent with main shop design
✅ Consistent with main storefront design
✅ Dark mode adapts to store colors
✅ Professional, polished appearance
@@ -959,17 +959,17 @@ No store_code needed! Store extracted from Referer header automatically.
Usage:
// Product catalog
const products = await fetch('/api/v1/shop/products');
const products = await fetch('/api/v1/storefront/products');
// Add to cart
const response = await fetch('/api/v1/shop/cart/session123/items', {
const response = await fetch('/api/v1/storefront/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', {
const order = await fetch('/api/v1/storefront/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData)
@@ -987,7 +987,7 @@ Features:
Location: app/static/shared/js/log-config.js
Shop-Specific Logging:
Storefront-Specific Logging:
shopLog.info('Product added to cart', product);
shopLog.error('Checkout failed', error);
shopLog.debug('Search query', { query, results });
@@ -1008,7 +1008,7 @@ 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:
Storefront Icons:
• shopping-cart, shopping-bag
• heart (wishlist)
• search, filter
@@ -1066,7 +1066,7 @@ Components:
• Category cards
• About store section
Data Sources:
• GET /api/v1/shop/products?is_featured=true
• GET /api/v1/storefront/products?is_featured=true
/products
──────────────────────────────────────────────────────────────────
@@ -1077,8 +1077,8 @@ Components:
• Sort dropdown
• Pagination
Data Sources:
• GET /api/v1/shop/products?skip=0&limit=20
• GET /api/v1/shop/products?search=query
• GET /api/v1/storefront/products?skip=0&limit=20
• GET /api/v1/storefront/products?search=query
• Filters applied client-side or server-side
/products/{product_id}
@@ -1091,8 +1091,8 @@ Components:
• Related products
• Reviews (optional)
Data Sources:
• GET /api/v1/shop/products/{id}
• GET /api/v1/shop/products?limit=4 (related products)
• GET /api/v1/storefront/products/{id}
• GET /api/v1/storefront/products?limit=4 (related products)
/cart
──────────────────────────────────────────────────────────────────
@@ -1116,7 +1116,7 @@ Components:
• Order summary
• Submit button
Data Sources:
• POST /api/v1/shop/orders
• POST /api/v1/storefront/orders
• Stripe/PayPal integration
/search
@@ -1128,7 +1128,7 @@ Components:
• Filter options
• Sort options
Data Sources:
• GET /api/v1/shop/products?search=query
• GET /api/v1/storefront/products?search=query
/category/{category_slug}
──────────────────────────────────────────────────────────────────
@@ -1139,7 +1139,7 @@ Components:
• Subcategories
• Filters
Data Sources:
• GET /api/v1/shop/products?category={slug}
• GET /api/v1/storefront/products?category={slug}
/about
──────────────────────────────────────────────────────────────────
@@ -1162,7 +1162,7 @@ Components:
• Business hours
• Social links
Data Sources:
• CMS content page (GET /api/v1/shop/content-pages/contact)
• CMS content page (GET /api/v1/storefront/content-pages/contact)
• Form submission to store email
@@ -1179,7 +1179,7 @@ For New Developers:
2. Study Existing Page (2 hours)
→ Open home.html
→ Open shop-layout.js
→ Open storefront-layout.js
→ Trace product loading flow
→ Examine cart management
@@ -1228,20 +1228,20 @@ Before Deploying:
Multi-Access Aware Error Pages:
All shop error pages (404, 500, etc.) are store-context aware and display
All storefront error pages (404, 500, etc.) are store-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
• app/templates/storefront/errors/404.html - Not Found
• app/templates/storefront/errors/400.html - Bad Request
• app/templates/storefront/errors/401.html - Unauthorized
• app/templates/storefront/errors/403.html - Forbidden
• app/templates/storefront/errors/422.html - Validation Error
• app/templates/storefront/errors/429.html - Rate Limited
• app/templates/storefront/errors/500.html - Server Error
• app/templates/storefront/errors/502.html - Bad Gateway
• app/templates/storefront/errors/base.html - Base error template
• app/templates/storefront/errors/generic.html - Generic error
Error Renderer (app/exceptions/error_renderer.py):
@@ -1252,7 +1252,7 @@ Calculates base_url dynamically based on store access method:
access_method = getattr(request.state, "access_method", None)
store_context = getattr(request.state, "store_context", None)
# Calculate base_url for shop links
# Calculate base_url for storefront links
base_url = "/"
if access_method == "path" and store:
full_prefix = store_context.get('full_prefix', '/store/')
@@ -1280,13 +1280,13 @@ All error page links use {{ base_url }} prefix for correct routing:
How It Works:
1. Error occurs (404, 500, etc.)
2. Exception handler detects shop context
2. Exception handler detects storefront context
3. error_renderer.py calculates base_url from store_context
4. Error template renders with correct base_url
5. Links work for all access methods:
- Domain: customshop.com → base_url = "/"
- Subdomain: orion.platform.com → base_url = "/"
- Path: localhost/stores/orion/ → base_url = "/stores/orion/"
- Path: localhost/storefront/orion/ → base_url = "/storefront/orion/"
Benefits:
✅ Error pages work correctly regardless of access method
@@ -1333,13 +1333,13 @@ Documentation:
• FastAPI: https://fastapi.tiangolo.com/
Internal Docs:
• Page Template Guide: FRONTEND_SHOP_ALPINE_PAGE_TEMPLATE.md
• Multi-Theme Guide: MULTI_THEME_SHOP_GUIDE.md
• Page Template Guide: FRONTEND_STOREFRONT_ALPINE_PAGE_TEMPLATE.md
• Multi-Theme Guide: MULTI_THEME_STOREFRONT_GUIDE.md
• API Documentation: API_REFERENCE.md
• Database Schema: DATABASE_SCHEMA.md
══════════════════════════════════════════════════════════════════
SHOP FRONTEND ARCHITECTURE
STOREFRONT FRONTEND ARCHITECTURE
Theme-Driven, Customer-Focused, Brand-Consistent
══════════════════════════════════════════════════════════════════