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:
313
docs/features/vendor-landing-pages.md
Normal file
313
docs/features/vendor-landing-pages.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
318
docs/frontend/shop/navigation-flow.md
Normal file
318
docs/frontend/shop/navigation-flow.md
Normal 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.
|
||||
Reference in New Issue
Block a user