feat: implement vendor landing pages with multi-template support and fix shop routing

Major improvements to shop URL routing and vendor landing page system:

## Landing Page System
- Add template field to ContentPage model for flexible landing page designs
- Create 4 landing page templates: default, minimal, modern, and full
- Implement smart root handler to serve landing pages or redirect to shop
- Add create_landing_page.py script for easy landing page management
- Support both domain/subdomain and path-based vendor access
- Add comprehensive landing page documentation

## Route Fixes
- Fix duplicate /shop prefix in shop_pages.py routes
- Correct product detail page routing (was /shop/shop/products/{id})
- Update all shop routes to work with router prefix mounting
- Remove unused public vendor endpoints (/api/v1/public/vendors)

## Template Link Corrections
- Fix all shop template links to include /shop/ prefix
- Update breadcrumb 'Home' links to point to vendor root (landing page)
- Update header navigation 'Home' link to point to vendor root
- Correct CMS page links in footer navigation
- Fix account, cart, and error page navigation links

## Navigation Architecture
- Establish two-tier navigation: landing page (/) and shop (/shop/)
- Document complete navigation flow and URL hierarchy
- Support for vendors with or without landing pages (auto-redirect fallback)
- Consistent breadcrumb and header navigation behavior

## Documentation
- Add vendor-landing-pages.md feature documentation
- Add navigation-flow.md with complete URL hierarchy
- Update shop architecture docs with error handling section
- Add orphaned docs to mkdocs.yml navigation
- Document multi-access routing patterns

## Database
- Migration f68d8da5315a: add template field to content_pages table
- Support template values: default, minimal, modern, full

This establishes a complete landing page system allowing vendors to have
custom marketing homepages separate from their e-commerce shop, with
flexible template options and proper navigation hierarchy.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-23 00:10:45 +01:00
parent d85a68f175
commit b7bf505a61
26 changed files with 1967 additions and 242 deletions

View File

@@ -108,7 +108,21 @@ Layer 1: ROUTES (FastAPI)
Purpose: Vendor Detection + Template Rendering
Location: app/routes/shop_pages.py
Example:
⚠️ 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):
@@ -164,31 +178,31 @@ Responsibilities:
The shop frontend supports THREE access methods:
1. **Custom Domain** (Production)
URL: https://customdomain.com/products
URL: https://customdomain.com/shop/products
- Vendor has their own domain
- base_url = "/"
- Links: /products, /about, /contact
- Links: /shop/products, /shop/about, /shop/contact
2. **Subdomain** (Production)
URL: https://wizamart.letzshop.com/products
URL: https://wizamart.letzshop.com/shop/products
- Vendor uses platform subdomain
- base_url = "/"
- Links: /products, /about, /contact
- Links: /shop/products, /shop/about, /shop/contact
3. **Path-Based** (Development/Testing)
URL: http://localhost:8000/vendor/wizamart/products
URL: http://localhost:8000/vendors/wizamart/shop/products
- Vendor accessed via path prefix
- base_url = "/vendor/wizamart/"
- Links: /vendor/wizamart/products, /vendor/wizamart/about
- base_url = "/vendors/wizamart/"
- Links: /vendors/wizamart/shop/products, /vendors/wizamart/shop/about
⚠️ CRITICAL: All template links MUST use {{ base_url }} prefix
⚠️ CRITICAL: All template links MUST use {{ base_url }}shop/ prefix
Example:
❌ BAD: <a href="/products">Products</a>
✅ GOOD: <a href="{{ base_url }}products">Products</a>
❌ BAD: <a href="{{ base_url }}products">Products</a>
✅ GOOD: <a href="{{ base_url }}shop/products">Products</a>
❌ BAD: <a href="/contact">Contact</a>
✅ GOOD: <a href="{{ base_url }}contact">Contact</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

View File

@@ -0,0 +1,318 @@
# Shop Navigation Flow
Complete guide to navigation structure and URL hierarchy with landing pages.
## URL Hierarchy
```
/ (Vendor Root) → Landing Page (if exists) OR redirect to /shop/
├── /shop/ → E-commerce Homepage (Product Catalog)
│ ├── /shop/products → Product Catalog (same as /shop/)
│ ├── /shop/products/{id} → Product Detail Page
│ ├── /shop/cart → Shopping Cart
│ ├── /shop/checkout → Checkout Process
│ ├── /shop/account/login → Customer Login
│ ├── /shop/account/register → Customer Registration
│ ├── /shop/account/dashboard → Customer Dashboard (auth required)
│ ├── /shop/about → CMS Content Page
│ ├── /shop/contact → CMS Content Page
│ └── /shop/{slug} → Other CMS Pages
```
## Navigation Patterns
### Pattern 1: With Landing Page (Recommended)
**URL Structure:**
```
customdomain.com/ → Landing Page (marketing/brand)
customdomain.com/shop/ → E-commerce Shop
customdomain.com/shop/products → Product Catalog
```
**Navigation Flow:**
1. User visits vendor domain → **Landing Page**
2. Clicks "Shop Now" → **/shop/** (product catalog)
3. Clicks "Home" in breadcrumb → **/** (back to landing page)
4. Clicks logo → **/** (back to landing page)
**Breadcrumb Example (on Products page):**
```
Home > Products
/ (Landing Page)
```
### Pattern 2: Without Landing Page (Auto-Redirect)
**URL Structure:**
```
customdomain.com/ → Redirects to /shop/
customdomain.com/shop/ → E-commerce Shop
customdomain.com/shop/products → Product Catalog
```
**Navigation Flow:**
1. User visits vendor domain → **Redirects to /shop/**
2. User browses shop
3. Clicks "Home" in breadcrumb → **/** (redirects to /shop/)
4. Clicks logo → **/** (redirects to /shop/)
**Breadcrumb Example (on Products page):**
```
Home > Products
/ → /shop/ (redirects)
```
## Link References
### Base URL Calculation
The `base_url` variable is calculated in `shop_pages.py:get_shop_context()`:
```python
# For domain/subdomain access
base_url = "/"
# Result: /shop/products, /shop/cart, etc.
# For path-based access
base_url = "/vendors/wizamart/"
# Result: /vendors/wizamart/shop/products, /vendors/wizamart/shop/cart, etc.
```
### Template Links
**Logo / Home Link (Header):**
```jinja2
{# Points to vendor root (landing page or shop) #}
<a href="{{ base_url }}shop/">{{ vendor.name }}</a>
```
**Breadcrumb Home Link:**
```jinja2
{# Points to vendor root (landing page) #}
<a href="{{ base_url }}">Home</a>
```
**Shop Links:**
```jinja2
{# All shop pages include /shop/ prefix #}
<a href="{{ base_url }}shop/products">Products</a>
<a href="{{ base_url }}shop/cart">Cart</a>
<a href="{{ base_url }}shop/checkout">Checkout</a>
```
**CMS Page Links:**
```jinja2
{# CMS pages are under /shop/ #}
<a href="{{ base_url }}shop/about">About</a>
<a href="{{ base_url }}shop/contact">Contact</a>
<a href="{{ base_url }}shop/{{ page.slug }}">{{ page.title }}</a>
```
## Complete URL Examples
### Path-Based Access (Development)
```
http://localhost:8000/vendors/wizamart/
├── / (root) → Landing Page OR redirect to shop
├── /shop/ → Shop Homepage
├── /shop/products → Product Catalog
├── /shop/products/4 → Product Detail
├── /shop/cart → Shopping Cart
├── /shop/checkout → Checkout
├── /shop/account/login → Customer Login
├── /shop/about → About Page (CMS)
└── /shop/contact → Contact Page (CMS)
```
### Subdomain Access (Production)
```
https://wizamart.platform.com/
├── / (root) → Landing Page OR redirect to shop
├── /shop/ → Shop Homepage
├── /shop/products → Product Catalog
├── /shop/products/4 → Product Detail
├── /shop/cart → Shopping Cart
├── /shop/checkout → Checkout
├── /shop/account/login → Customer Login
├── /shop/about → About Page (CMS)
└── /shop/contact → Contact Page (CMS)
```
### Custom Domain Access (Production)
```
https://customdomain.com/
├── / (root) → Landing Page OR redirect to shop
├── /shop/ → Shop Homepage
├── /shop/products → Product Catalog
├── /shop/products/4 → Product Detail
├── /shop/cart → Shopping Cart
├── /shop/checkout → Checkout
├── /shop/account/login → Customer Login
├── /shop/about → About Page (CMS)
└── /shop/contact → Contact Page (CMS)
```
## User Journeys
### Journey 1: First-Time Visitor (With Landing Page)
1. Visit `customdomain.com/`**Landing Page**
- Sees brand story, features, CTA
2. Clicks "Shop Now" → `/shop/`**Product Catalog**
- Browses products
3. Clicks product → `/shop/products/4`**Product Detail**
- Views product
4. Clicks "Home" in breadcrumb → `/`**Back to Landing Page**
5. Clicks logo → `/`**Back to Landing Page**
### Journey 2: Returning Customer (Direct to Shop)
1. Visit `customdomain.com/shop/`**Product Catalog**
- Already knows the brand, goes straight to shop
2. Adds to cart → `/shop/cart`**Shopping Cart**
3. Checkout → `/shop/checkout`**Checkout**
4. Clicks "Home" → `/`**Landing Page** (brand homepage)
### Journey 3: Customer Account Management
1. Visit `customdomain.com/shop/account/login`**Login**
2. After login → `/shop/account/dashboard`**Dashboard**
3. View orders → `/shop/account/orders`**Order History**
4. Clicks logo → `/`**Back to Landing Page**
## Navigation Components
### Header Navigation (base.html)
```jinja2
{# Logo - always points to vendor root #}
<a href="{{ base_url }}shop/">
<img src="{{ theme.branding.logo }}" alt="{{ vendor.name }}">
</a>
{# Main Navigation #}
<nav>
<a href="{{ base_url }}shop/">Home</a>
<a href="{{ base_url }}shop/products">Products</a>
<a href="{{ base_url }}shop/about">About</a>
<a href="{{ base_url }}shop/contact">Contact</a>
</nav>
{# Actions #}
<a href="{{ base_url }}shop/cart">Cart</a>
<a href="{{ base_url }}shop/account">Account</a>
```
### Footer Navigation (base.html)
```jinja2
{# Quick Links #}
<a href="{{ base_url }}shop/products">Products</a>
{# CMS Pages (dynamic) #}
{% for page in footer_pages %}
<a href="{{ base_url }}shop/{{ page.slug }}">{{ page.title }}</a>
{% endfor %}
```
### Breadcrumbs (products.html, content-page.html)
```jinja2
{# Points to vendor root (landing page) #}
<a href="{{ base_url }}">Home</a> / Products
```
## Best Practices
### ✅ DO:
1. **Use Landing Pages**: Create engaging landing pages at vendor root
2. **Clear Navigation**: Make it easy to get from landing to shop and back
3. **Consistent "Home"**: Logo and "Home" breadcrumb both point to `/` (landing)
4. **Shop Links**: All shop-related links include `/shop/` prefix
5. **CMS Under Shop**: Keep CMS pages under `/shop/` for consistency
### ❌ DON'T:
1. **Hardcode URLs**: Always use `{{ base_url }}` for vendor-aware links
2. **Skip /shop/**: Don't link directly to `/products`, use `/shop/products`
3. **Mix Landing & Shop**: Keep landing page separate from shop catalog
4. **Forget Breadcrumbs**: Always provide "Home" link to go back
## Migration Notes
### Before Landing Pages
All links pointed to `/shop/`:
```jinja2
<a href="{{ base_url }}shop/">Home</a> {# Logo #}
<a href="{{ base_url }}shop/">Home</a> {# Breadcrumb #}
```
### After Landing Pages
Separation of concerns:
```jinja2
<a href="{{ base_url }}shop/">Home</a> {# Logo - still goes to shop #}
<a href="{{ base_url }}">Home</a> {# Breadcrumb - goes to landing #}
```
This allows:
- Landing page at `/` for marketing/branding
- Shop catalog at `/shop/` for e-commerce
- Clean navigation between the two
## Technical Implementation
### Route Handlers (main.py)
```python
# Vendor root - serves landing page or redirects to shop
@app.get("/")
@app.get("/vendors/{vendor_code}/")
async def root(request: Request):
if has_landing_page():
return render_landing_page()
else:
return redirect_to_shop()
# Shop routes
@app.include_router(shop_pages.router, prefix="/shop")
@app.include_router(shop_pages.router, prefix="/vendors/{vendor_code}/shop")
```
### Context Calculation (shop_pages.py)
```python
def get_shop_context(request: Request):
base_url = "/"
if access_method == "path":
base_url = f"/vendors/{vendor.subdomain}/"
return {
"base_url": base_url,
"vendor": vendor,
"theme": theme,
# ...
}
```
## Summary
The navigation system creates a **two-tier structure**:
1. **Landing Page** (`/`) - Marketing, branding, vendor story
2. **Shop** (`/shop/`) - E-commerce, products, cart, checkout
This gives vendors flexibility to:
- Have a marketing homepage separate from their store
- Choose different landing page designs (minimal, modern, full)
- Or skip the landing page and go straight to the shop
All while maintaining clean, consistent navigation throughout the experience.