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

@@ -0,0 +1,313 @@
# Vendor Landing Pages
Complete guide to creating custom landing pages for vendor storefronts.
## Overview
Each vendor can have a custom landing page at their root URL with different design templates. This landing page serves as the vendor's homepage, separate from the e-commerce shop section.
### URL Structure
```
Root Landing Page:
- Custom Domain: https://customdomain.com/ → Landing Page
- Subdomain: https://wizamart.platform.com/ → Landing Page
- Path-based: http://localhost:8000/vendors/wizamart/ → Landing Page
E-commerce Shop:
- Custom Domain: https://customdomain.com/shop/ → Shop Homepage
- Subdomain: https://wizamart.platform.com/shop/ → Shop Homepage
- Path-based: http://localhost:8000/vendors/wizamart/shop/ → Shop Homepage
```
## Features
**Multiple Templates**: Choose from 4 different landing page designs
**CMS-Powered**: Content managed through ContentPage model
**Per-Vendor Customization**: Each vendor can have unique design
**Auto-Fallback**: Redirects to shop if no landing page exists
**Theme-Aware**: Uses vendor's theme colors and branding
## Available Templates
### 1. **Default** (`landing-default.html`)
- Clean and professional
- Hero section with logo and CTA
- Optional content section
- Quick links grid (3 cards)
- **Best for**: General purpose, simple storefronts
### 2. **Minimal** (`landing-minimal.html`)
- Ultra-simple centered design
- Single page, no scrolling
- One primary CTA
- Minimal navigation
- **Best for**: Single-product vendors, portfolio sites
### 3. **Modern** (`landing-modern.html`)
- Full-screen hero with animations
- Feature showcase section
- Gradient backgrounds
- Multiple CTAs
- **Best for**: Tech products, modern brands
### 4. **Full** (`landing-full.html`)
- Split-screen hero layout
- Stats/badges section
- 4-feature grid
- Multiple content sections
- Final CTA section
- **Best for**: Established brands, full product catalogs
## How to Create a Landing Page
### Step 1: Create ContentPage in Database
```python
# Using Python/SQL
from models.database.content_page import ContentPage
landing_page = ContentPage(
vendor_id=1, # Your vendor ID
slug="landing", # Must be "landing" or "home"
title="Welcome to Our Store",
content="<p>Your custom HTML content here...</p>",
template="modern", # Choose: default, minimal, modern, full
is_published=True,
show_in_footer=False,
show_in_header=False
)
db.add(landing_page)
db.commit()
```
### Step 2: Choose Template
Set the `template` field to one of:
- `"default"` - Clean professional layout
- `"minimal"` - Ultra-simple centered design
- `"modern"` - Full-screen with animations
- `"full"` - Maximum features and sections
### Step 3: Add Content (Optional)
The `content` field supports HTML. This appears in a dedicated section on all templates:
```html
<h2>About Our Store</h2>
<p>We've been serving customers since 2020...</p>
<ul>
<li>Quality products</li>
<li>Fast shipping</li>
<li>Great customer service</li>
</ul>
```
### Step 4: Publish
Set `is_published=True` to make the landing page live.
## Fallback Behavior
If no landing page exists:
1. System checks for `slug="landing"`
2. If not found, checks for `slug="home"`
3. If neither exists, **redirects to `/shop/`**
This ensures vendors always have a working homepage even without a landing page.
## Template Variables
All templates have access to:
### From Request Context
```python
{
"vendor": Vendor object,
"theme": Theme object with colors/branding,
"base_url": "/" or "/vendors/{code}/",
"page": ContentPage object,
"header_pages": List of header navigation pages,
"footer_pages": List of footer navigation pages
}
```
### Vendor Properties
```jinja2
{{ vendor.name }}
{{ vendor.tagline }}
{{ vendor.description }}
{{ vendor.website }}
```
### Theme Properties
```jinja2
{{ theme.branding.logo }}
{{ theme.branding.logo_dark }}
{{ theme.colors.primary }}
{{ theme.colors.accent }}
```
### Page Properties
```jinja2
{{ page.title }}
{{ page.content | safe }}
{{ page.meta_description }}
{{ page.template }}
```
## Migration Applied
Database migration `f68d8da5315a` adds `template` field to `content_pages` table:
```sql
ALTER TABLE content_pages
ADD COLUMN template VARCHAR(50) NOT NULL DEFAULT 'default';
```
## API Endpoints
### Get Landing Page
```http
GET /api/v1/shop/content-pages/landing
Referer: https://wizamart.platform.com/
Response:
{
"id": 1,
"slug": "landing",
"title": "Welcome to WizaMart",
"content": "<p>...</p>",
"template": "modern",
"is_published": true
}
```
## Testing
### Test Landing Page
1. Create a landing page via database or admin panel
2. Access vendor root URL:
- Path-based: `http://localhost:8000/vendors/wizamart/`
- Subdomain: `https://wizamart.platform.com/`
3. Should see your custom landing page
### Test Fallback
1. Delete or unpublish landing page
2. Access vendor root URL
3. Should redirect to `/shop/`
## Customization Guide
### Adding a New Template
1. Create new template file:
```
app/templates/vendor/landing-{name}.html
```
2. Extend shop base:
```jinja2
{% extends "shop/base.html" %}
```
3. Use template variables as needed
4. Update `page.template` to `{name}` in database
### Modifying Existing Templates
Templates are in:
```
app/templates/vendor/
├── landing-default.html (Clean professional)
├── landing-minimal.html (Ultra-simple)
├── landing-modern.html (Full-screen hero)
└── landing-full.html (Maximum features)
```
Edit directly and changes apply immediately (no rebuild needed).
## Best Practices
1. **Choose Template Based on Content**:
- Minimal: Little content, single CTA
- Default: Moderate content, professional
- Modern: Tech/modern brands, animated
- Full: Lots of content, established brand
2. **Keep Content Concise**: Landing pages should be scannable
3. **Strong CTAs**: Always link to `/shop/` for e-commerce
4. **Use Theme Colors**: Templates automatically use vendor theme
5. **Test Responsiveness**: All templates are mobile-friendly
6. **SEO**: Fill in `meta_description` for better search results
## Examples
### Example 1: Simple Store
```python
ContentPage(
vendor_id=1,
slug="landing",
title="Welcome to TechStore",
content="<p>Your one-stop shop for electronics</p>",
template="default",
is_published=True
)
```
### Example 2: Portfolio Site
```python
ContentPage(
vendor_id=2,
slug="landing",
title="John's Artwork",
content="<p>Handcrafted pieces since 2015</p>",
template="minimal",
is_published=True
)
```
### Example 3: Modern Brand
```python
ContentPage(
vendor_id=3,
slug="landing",
title="FutureWear - Style Redefined",
content="<h2>Our Story</h2><p>Innovation meets fashion...</p>",
template="modern",
is_published=True
)
```
## Troubleshooting
### Landing Page Not Showing
- Check `is_published=True`
- Verify `slug="landing"` or `slug="home"`
- Check vendor ID matches
- Verify template field is valid
### Wrong Template Rendering
- Check `template` field value
- Ensure template file exists at `app/templates/vendor/landing-{template}.html`
- Check for typos in template name
### Theme Colors Not Applied
- Verify vendor has theme configured
- Check CSS variables in template: `var(--color-primary)`
- Inspect theme object in template context
## Future Enhancements
**Phase 2: Section Builder** (Future)
- Drag-and-drop section components
- Hero, Features, Testimonials, Gallery sections
- Per-section customization
- Visual page builder interface
This will allow vendors to build completely custom layouts without template limitations.

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.