Files
orion/docs/features/cms-implementation-guide.md
Samir Boulahtit be07bbb3b1 docs: add comprehensive CMS documentation
Add complete documentation for the Content Management System:

Feature Documentation (docs/features/):
- content-management-system.md - Complete CMS overview
  * Two-tier architecture explanation
  * Database schema and relationships
  * API reference for all endpoints
  * Usage workflows for admin/vendor/customer
  * Best practices and examples

- cms-implementation-guide.md - Step-by-step implementation
  * Activation checklist
  * Code integration instructions
  * Testing procedures
  * Troubleshooting guide

Quick Start Guide (docs/getting-started/):
- cms-quick-start.md - Quick reference
  * Setup commands
  * Access URLs
  * Managing content (API, admin panel, vendor dashboard)
  * Two-tier system explained with examples
  * Common tasks and troubleshooting

Updated Seeder Docs:
- Add CMS to enhanced seeder coverage list
- Add dedicated CMS section with table of pages
- Document integration with db-setup workflow
- Update best practices

MkDocs Configuration:
- Add "Features" section with CMS documentation
- Add CMS Quick Start to "Getting Started"
- Add CDN Fallback Strategy to "Frontend Development"
- Complete navigation structure

All documentation builds cleanly with no warnings.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 15:55:42 +01:00

11 KiB

CMS Implementation Guide

Quick Start

This guide shows you how to implement the Content Management System for static pages.

What Was Implemented

Database Model: models/database/content_page.py Service Layer: app/services/content_page_service.py Admin API: app/api/v1/admin/content_pages.py Vendor API: app/api/v1/vendor/content_pages.py Shop API: app/api/v1/shop/content_pages.py Documentation: Full CMS documentation in docs/features/content-management-system.md

Next Steps to Activate

1. Create Database Migration

# Create Alembic migration
alembic revision --autogenerate -m "Add content_pages table"

# Review the generated migration in alembic/versions/

# Run migration
alembic upgrade head

2. Add Relationship to Vendor Model

Edit models/database/vendor.py and add this relationship:

# Add this import
from sqlalchemy.orm import relationship

# Add this relationship to Vendor class
content_pages = relationship("ContentPage", back_populates="vendor", cascade="all, delete-orphan")

3. Register API Routers

Edit the appropriate router files to include the new endpoints:

Admin Router (app/api/v1/admin/__init__.py):

from app.api.v1.admin import content_pages

api_router.include_router(
    content_pages.router,
    prefix="/content-pages",
    tags=["admin-content-pages"]
)

Vendor Router (app/api/v1/vendor/__init__.py):

from app.api.v1.vendor import content_pages

api_router.include_router(
    content_pages.router,
    prefix="/{vendor_code}/content-pages",
    tags=["vendor-content-pages"]
)

Shop Router (app/api/v1/shop/__init__.py or create if doesn't exist):

from app.api.v1.shop import content_pages

api_router.include_router(
    content_pages.router,
    prefix="/content-pages",
    tags=["shop-content-pages"]
)

4. Update Shop Routes to Use CMS

Edit app/routes/shop_pages.py to add a generic content page handler:

from app.services.content_page_service import content_page_service

@router.get("/{slug}", response_class=HTMLResponse, include_in_schema=False)
async def generic_content_page(
    slug: str,
    request: Request,
    db: Session = Depends(get_db)
):
    """
    Generic content page handler.
    Handles: /about, /faq, /contact, /shipping, /returns, /privacy, /terms, etc.
    """
    vendor = getattr(request.state, 'vendor', None)
    vendor_id = vendor.id if vendor else None

    page = content_page_service.get_page_for_vendor(
        db,
        slug=slug,
        vendor_id=vendor_id,
        include_unpublished=False
    )

    if not page:
        raise HTTPException(status_code=404, detail=f"Page not found: {slug}")

    return templates.TemplateResponse(
        "shop/content-page.html",
        get_shop_context(request, page=page)
    )

5. Create Generic Content Page Template

Create app/templates/shop/content-page.html:

{# app/templates/shop/content-page.html #}
{% extends "shop/base.html" %}

{% block title %}{{ page.title }}{% endblock %}

{% block meta_description %}
  {{ page.meta_description or page.title }}
{% endblock %}

{% block content %}
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">

  {# Breadcrumbs #}
  <nav class="mb-6 text-sm">
    <a href="{{ base_url }}" class="text-primary hover:underline">Home</a>
    <span class="mx-2 text-gray-400">/</span>
    <span class="text-gray-600 dark:text-gray-300">{{ page.title }}</span>
  </nav>

  {# Page Title #}
  <h1 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-8">
    {{ page.title }}
  </h1>

  {# Content #}
  <div class="prose prose-lg dark:prose-invert max-w-none">
    {{ page.content | safe }}
  </div>

  {# Last updated #}
  {% if page.updated_at %}
  <div class="mt-12 pt-6 border-t border-gray-200 dark:border-gray-700 text-sm text-gray-500 dark:text-gray-400">
    Last updated: {{ page.updated_at.strftime('%B %d, %Y') }}
  </div>
  {% endif %}

</div>
{% endblock %}

Edit app/templates/shop/base.html to load navigation from database.

First, update the context helper to include footer pages:

# app/routes/shop_pages.py

def get_shop_context(request: Request, **extra_context) -> dict:
    # ... existing code ...

    # Load footer navigation pages
    db = next(get_db())
    try:
        footer_pages = content_page_service.list_pages_for_vendor(
            db,
            vendor_id=vendor.id if vendor else None,
            include_unpublished=False,
            footer_only=True
        )
    finally:
        db.close()

    context = {
        "request": request,
        "vendor": vendor,
        "theme": theme,
        "clean_path": clean_path,
        "access_method": access_method,
        "base_url": base_url,
        "footer_pages": footer_pages,  # Add this
        **extra_context
    }

    return context

Then update the footer template:

{# app/templates/shop/base.html - Footer section #}

<div>
    <h4 class="font-semibold mb-4">Quick Links</h4>
    <ul class="space-y-2">
        {% for page in footer_pages %}
        <li>
            <a href="{{ base_url }}{{ page.slug }}"
               class="text-gray-600 hover:text-primary dark:text-gray-400">
                {{ page.title }}
            </a>
        </li>
        {% endfor %}
    </ul>
</div>

7. Create Default Platform Pages (Script)

Create scripts/create_default_content_pages.py:

#!/usr/bin/env python3
"""Create default platform content pages."""

from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.services.content_page_service import content_page_service

def create_defaults():
    db: Session = SessionLocal()

    try:
        # About Us
        content_page_service.create_page(
            db,
            slug="about",
            title="About Us",
            content="""
                <h2>Welcome to Our Marketplace</h2>
                <p>We connect quality vendors with customers worldwide.</p>
                <p>Our mission is to provide a seamless shopping experience...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=1
        )

        # Shipping Information
        content_page_service.create_page(
            db,
            slug="shipping",
            title="Shipping Information",
            content="""
                <h2>Shipping Policy</h2>
                <p>We offer fast and reliable shipping...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=2
        )

        # Returns
        content_page_service.create_page(
            db,
            slug="returns",
            title="Returns & Refunds",
            content="""
                <h2>Return Policy</h2>
                <p>30-day return policy on all items...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=3
        )

        # Privacy Policy
        content_page_service.create_page(
            db,
            slug="privacy",
            title="Privacy Policy",
            content="""
                <h2>Privacy Policy</h2>
                <p>Your privacy is important to us...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=4
        )

        # Terms of Service
        content_page_service.create_page(
            db,
            slug="terms",
            title="Terms of Service",
            content="""
                <h2>Terms of Service</h2>
                <p>By using our platform, you agree to...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=5
        )

        # Contact
        content_page_service.create_page(
            db,
            slug="contact",
            title="Contact Us",
            content="""
                <h2>Get in Touch</h2>
                <p>Have questions? We'd love to hear from you!</p>
                <p>Email: support@example.com</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=6
        )

        # FAQ
        content_page_service.create_page(
            db,
            slug="faq",
            title="Frequently Asked Questions",
            content="""
                <h2>FAQ</h2>
                <h3>How do I place an order?</h3>
                <p>Simply browse our products...</p>
            """,
            is_published=True,
            show_in_footer=True,
            display_order=7
        )

        print("✅ Created default content pages successfully")

    except Exception as e:
        print(f"❌ Error: {e}")
        db.rollback()
    finally:
        db.close()


if __name__ == "__main__":
    create_defaults()

Run it:

python scripts/create_default_content_pages.py

Testing

1. Test Platform Defaults

# Create platform default
curl -X POST http://localhost:8000/api/v1/admin/content-pages/platform \
  -H "Authorization: Bearer <admin_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "about",
    "title": "About Our Marketplace",
    "content": "<h1>About</h1><p>Platform default content</p>",
    "is_published": true,
    "show_in_footer": true
  }'

# View in shop
curl http://localhost:8000/vendor/wizamart/about

2. Test Vendor Override

# Create vendor override
curl -X POST http://localhost:8000/api/v1/vendor/wizamart/content-pages/ \
  -H "Authorization: Bearer <vendor_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "about",
    "title": "About Wizamart",
    "content": "<h1>About Wizamart</h1><p>Custom vendor content</p>",
    "is_published": true
  }'

# View in shop (should show vendor content)
curl http://localhost:8000/vendor/wizamart/about

3. Test Fallback

# Delete vendor override
curl -X DELETE http://localhost:8000/api/v1/vendor/wizamart/content-pages/{id} \
  -H "Authorization: Bearer <vendor_token>"

# View in shop (should fall back to platform default)
curl http://localhost:8000/vendor/wizamart/about

Summary

You now have a complete CMS system that allows:

  1. Platform admins to create default content for all vendors
  2. Vendors to override specific pages with custom content
  3. Automatic fallback to platform defaults when vendor hasn't customized
  4. Dynamic navigation loading from database
  5. SEO optimization with meta tags
  6. Draft/Published workflow for content management

All pages are accessible via their slug: /about, /faq, /contact, etc. with proper vendor context and routing support!