- Add loyalty program analysis document - Add multi-platform CMS architecture proposal - Document three-tier content hierarchy - Include URL/access patterns for all personas - Define migration strategy and implementation phases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
24 KiB
Multi-Platform CMS Architecture
Session Date: 2026-01-18 Status: Initial Analysis - Requirements Captured Related: Loyalty Program Analysis
Executive Summary
The platform is evolving from a single OMS product to a multi-platform business where each platform represents a distinct business offering (OMS, Loyalty Program, Website Builder, etc.). Each platform requires its own independent CMS with a three-tier content hierarchy:
- Platform Pages - Marketing site for the platform itself
- Vendor Default Pages - Fallback content for vendor storefronts
- Vendor Override/Custom Pages - Vendor-specific content
Current State Analysis
Problems Identified
| Issue | Description |
|---|---|
| Conflated page types | Platform pages and vendor defaults share vendor_id = NULL, making them indistinguishable |
| Hardcoded homepage | Platform homepage uses homepage-wizamart.html directly, ignoring CMS |
| Non-functional admin UI | /admin/platform-homepage saves to CMS but route doesn't use it |
| Single platform assumption | Architecture assumes one platform, can't scale to multiple offerings |
| No platform isolation | No way to have separate About/FAQ/Pricing pages per platform |
Current Architecture (Broken)
ContentPage (vendor_id = NULL)
↓ used by both (conflated)
├── Platform Homepage (/about, /pricing) ← Should be Platform A specific
└── Vendor Default Fallback ← Should be generic storefront pages
Proposed Architecture
Multi-Platform Hierarchy
┌─────────────────────────────────────────────────────────────────────────────┐
│ PLATFORM LEVEL │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Platform A │ │ Platform B │ │ Platform C │ │
│ │ (Wizamart OMS) │ │ (Loyalty+) │ │ (Site Builder) │ │
│ │ │ │ │ │ │ │
│ │ • Homepage │ │ • Homepage │ │ • Homepage │ │
│ │ • About │ │ • About │ │ • About │ │
│ │ • Pricing │ │ • Pricing │ │ • Pricing │ │
│ │ • FAQ │ │ • FAQ │ │ • FAQ │ │
│ │ • Contact │ │ • Contact │ │ • Contact │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ VENDOR DEFAULT LEVEL (per platform) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Platform A Defaults │ │
│ │ • About Us (generic store template) │ │
│ │ • Shipping Policy │ │
│ │ • Return Policy │ │
│ │ • Privacy Policy │ │
│ │ • Terms of Service │ │
│ │ • FAQ (e-commerce focused) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ VENDOR LEVEL (isolated) │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Vendor 1 (WizaMart) │ │ Vendor 2 (TechStore) │ │
│ │ Platform A, Tier: Pro │ │ Platform A, Tier: Basic │ │
│ │ │ │ │ │
│ │ Override Pages: │ │ Override Pages: │ │
│ │ • About (custom) │ │ • (none - uses defaults)│ │
│ │ • Shipping (custom) │ │ │ │
│ │ │ │ Custom Pages: │ │
│ │ Custom Pages: │ │ • Size Guide │ │
│ │ • Our Story │ │ │ │
│ │ • Store Locations │ │ │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Content Resolution Flow
When a customer visits vendor1.example.com/about:
1. Identify vendor context (Vendor 1)
2. Identify platform context (Platform A)
3. Check: Does Vendor 1 have custom "about" page?
├── YES → Return vendor's custom page
└── NO → Check: Does Platform A have default "about" page?
├── YES → Return platform default
└── NO → Return 404
Data Model Changes
New: Platform Model
class Platform(Base):
"""
Represents a business offering/product line.
Examples: Wizamart OMS, Loyalty+, Site Builder
"""
__tablename__ = "platforms"
id = Column(Integer, primary_key=True)
code = Column(String(50), unique=True, nullable=False) # "oms", "loyalty", "sites"
name = Column(String(100), nullable=False) # "Wizamart OMS"
domain = Column(String(255), nullable=True) # "wizamart.lu"
# Branding
logo = Column(String(500), nullable=True)
theme_config = Column(JSON, nullable=True) # Colors, fonts, etc.
# Status
is_active = Column(Boolean, default=True)
# Timestamps
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
# Relationships
content_pages = relationship("ContentPage", back_populates="platform")
subscription_tiers = relationship("SubscriptionTier", back_populates="platform")
vendors = relationship("Vendor", back_populates="platform")
Updated: ContentPage Model
class ContentPage(Base):
"""
CMS content page with three-tier hierarchy:
1. Platform pages (platform_id set, vendor_id NULL, is_platform_page=True)
2. Vendor defaults (platform_id set, vendor_id NULL, is_platform_page=False)
3. Vendor overrides (platform_id set, vendor_id set)
"""
__tablename__ = "content_pages"
id = Column(Integer, primary_key=True)
# NEW: Platform association (required)
platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
# Existing: Vendor association (NULL for platform pages and defaults)
vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=True)
# NEW: Distinguish platform marketing pages from vendor defaults
is_platform_page = Column(Boolean, default=False, nullable=False)
# True = Platform's own page (homepage, pricing, platform about)
# False = Vendor default template (when vendor_id is NULL)
# N/A = Vendor override (when vendor_id is set)
# Existing fields...
slug = Column(String(100), nullable=False)
title = Column(String(200), nullable=False)
content = Column(Text, nullable=False)
content_format = Column(String(20), default="html")
template = Column(String(50), default="default")
# SEO
meta_description = Column(String(300), nullable=True)
meta_keywords = Column(String(300), nullable=True)
# Publishing
is_published = Column(Boolean, default=False)
published_at = Column(DateTime, nullable=True)
# Navigation
display_order = Column(Integer, default=0)
show_in_header = Column(Boolean, default=False)
show_in_footer = Column(Boolean, default=True)
show_in_legal = Column(Boolean, default=False)
# Timestamps & audit
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
updated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
# Constraints
__table_args__ = (
UniqueConstraint("platform_id", "vendor_id", "slug", name="uq_platform_vendor_slug"),
Index("idx_platform_vendor_published", "platform_id", "vendor_id", "is_published"),
)
Updated: Vendor Model
class Vendor(Base):
# Existing fields...
# NEW: Platform association
platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
platform = relationship("Platform", back_populates="vendors")
Page Type Matrix
| Page Type | platform_id | vendor_id | is_platform_page | Example |
|---|---|---|---|---|
| Platform Marketing Page | ✓ | NULL | TRUE | Platform A's homepage, pricing |
| Vendor Default Page | ✓ | NULL | FALSE | Generic "About Our Store" template |
| Vendor Override Page | ✓ | ✓ | FALSE | WizaMart's custom About page |
| Vendor Custom Page | ✓ | ✓ | FALSE | WizaMart's "Store Locations" page |
User Journeys
Journey 1: Platform Admin Sets Up New Platform
Actor: Super Admin Goal: Create a new business platform with its marketing pages
1. Admin navigates to /admin/platforms
2. Admin clicks "Create Platform"
3. Admin fills in:
- Code: "loyalty"
- Name: "Loyalty+"
- Domain: "loyalty.wizamart.lu"
- Logo, theme colors
4. Admin saves platform
5. System creates platform record
6. Admin navigates to /admin/platforms/loyalty/pages
7. Admin creates platform pages:
- Homepage (is_platform_page=True)
- About Us (is_platform_page=True)
- Pricing (is_platform_page=True)
- FAQ (is_platform_page=True)
- Contact (is_platform_page=True)
8. Each page can use different templates (modern, minimal, etc.)
9. Admin publishes pages
10. Platform marketing site is now live at loyalty.wizamart.lu
Journey 2: Platform Admin Creates Vendor Defaults
Actor: Platform Admin Goal: Set up default storefront pages for all vendors on Platform A
1. Admin navigates to /admin/platforms/oms/vendor-defaults
2. Admin sees list of vendor default pages
3. Admin creates default pages:
- About Us (generic store template)
Content: "Welcome to our store. We're dedicated to..."
- Shipping Policy
Content: "We offer fast and reliable shipping..."
- Return Policy
Content: "30-day return policy on all items..."
- Privacy Policy
Content: "Your privacy is important to us..."
- Terms of Service
Content: "By using our store, you agree to..."
4. All pages have is_platform_page=False, vendor_id=NULL
5. These pages are now available to ALL vendors on Platform A
6. Vendors who don't customize will see these defaults
Journey 3: Vendor Subscribes and Views Default Pages
Actor: New Vendor (TechStore) Goal: Start using the platform and see what pages are available
1. Vendor signs up for Platform A (OMS), selects "Basic" tier
2. Vendor completes onboarding
3. Vendor logs into dashboard
4. Vendor navigates to "Content Pages" section
5. Vendor sees list of pages:
┌─────────────────────────────────────────────────────────┐
│ Content Pages │
├─────────────────────────────────────────────────────────┤
│ Page │ Source │ Status │ Actions │
├───────────────┼──────────────────┼───────────┼─────────┤
│ About Us │ Platform Default │ Published │ Override│
│ Shipping │ Platform Default │ Published │ Override│
│ Returns │ Platform Default │ Published │ Override│
│ Privacy │ Platform Default │ Published │ Override│
│ Terms │ Platform Default │ Published │ Override│
└─────────────────────────────────────────────────────────┘
[+ Create Custom Page]
6. Vendor previews storefront at techstore.example.com/about
7. Sees platform default "About Us" content
Journey 4: Vendor Overrides a Default Page
Actor: Vendor (WizaMart) Goal: Customize the About page with store-specific content
1. Vendor logs into dashboard
2. Navigates to Content Pages
3. Sees "About Us" with source "Platform Default"
4. Clicks "Override" button
5. System creates a copy with vendor_id set
6. Vendor edits content:
- Title: "About WizaMart"
- Content: "WizaMart was founded in 2020 in Luxembourg..."
- Adds store images
7. Vendor saves and publishes
8. Page list now shows:
┌─────────────────────────────────────────────────────────┐
│ About Us │ Custom Override │ Published │ Edit │
└─────────────────────────────────────────────────────────┘
9. Customer visits wizamart.example.com/about
10. Sees WizaMart's custom About page
Journey 5: Vendor Creates a Custom Page
Actor: Vendor (WizaMart) Goal: Add a new page that doesn't exist in defaults
1. Vendor logs into dashboard
2. Navigates to Content Pages
3. Clicks "+ Create Custom Page"
4. Fills in:
- Slug: "store-locations"
- Title: "Our Store Locations"
- Content: Map and addresses of physical stores
- Show in footer: Yes
5. Vendor saves and publishes
6. Page appears in storefront footer navigation
7. Accessible at wizamart.example.com/store-locations
Journey 6: Vendor Reverts Override to Default
Actor: Vendor (WizaMart) Goal: Remove customization and use platform default again
1. Vendor navigates to Content Pages
2. Sees "Shipping" with source "Custom Override"
3. Clicks "Revert to Default"
4. System shows confirmation:
"This will delete your custom Shipping page and show
the platform default instead. This cannot be undone."
5. Vendor confirms
6. System deletes vendor's custom page
7. Storefront now shows platform default Shipping page
Journey 7: Customer Browses Vendor Storefront
Actor: Customer Goal: Read store policies before purchasing
1. Customer visits wizamart.example.com
2. Browses products, adds to cart
3. Wants to check return policy
4. Clicks "Returns" in footer
5. System resolves page:
- Check: WizaMart override? NO
- Check: Platform A default? YES
- Serve: Platform A default "Return Policy" page
6. Customer reads return policy
7. Customer clicks "About Us" in footer
8. System resolves page:
- Check: WizaMart override? YES
- Serve: WizaMart's custom "About WizaMart" page
9. Customer sees store-specific About page
Workflow Diagrams
Content Resolution Algorithm
resolve_page(vendor, slug):
│
├─► Get vendor's platform_id
│
├─► Query: ContentPage WHERE
│ platform_id = vendor.platform_id
│ AND vendor_id = vendor.id
│ AND slug = slug
│ AND is_published = True
│
├─► Found? ──YES──► Return vendor's page
│ │
│ NO
│ ▼
├─► Query: ContentPage WHERE
│ platform_id = vendor.platform_id
│ AND vendor_id IS NULL
│ AND is_platform_page = False ← Important: exclude platform pages
│ AND slug = slug
│ AND is_published = True
│
├─► Found? ──YES──► Return platform default
│ │
│ NO
│ ▼
└─► Return 404
Platform Page Resolution (Marketing Site)
resolve_platform_page(platform, slug):
│
├─► Query: ContentPage WHERE
│ platform_id = platform.id
│ AND vendor_id IS NULL
│ AND is_platform_page = True ← Only platform marketing pages
│ AND slug = slug
│ AND is_published = True
│
├─► Found? ──YES──► Return platform page
│ │
│ NO
│ ▼
└─► Return 404
API Endpoints
Platform Admin API
# Platform Management
GET /api/v1/admin/platforms # List all platforms
POST /api/v1/admin/platforms # Create platform
GET /api/v1/admin/platforms/{code} # Get platform
PUT /api/v1/admin/platforms/{code} # Update platform
DELETE /api/v1/admin/platforms/{code} # Delete platform
# Platform Pages (Marketing)
GET /api/v1/admin/platforms/{code}/pages # List platform pages
POST /api/v1/admin/platforms/{code}/pages # Create platform page
PUT /api/v1/admin/platforms/{code}/pages/{id} # Update platform page
DELETE /api/v1/admin/platforms/{code}/pages/{id} # Delete platform page
# Vendor Defaults
GET /api/v1/admin/platforms/{code}/defaults # List vendor defaults
POST /api/v1/admin/platforms/{code}/defaults # Create vendor default
PUT /api/v1/admin/platforms/{code}/defaults/{id}# Update vendor default
DELETE /api/v1/admin/platforms/{code}/defaults/{id}# Delete vendor default
Vendor API
# View All Pages (defaults + overrides + custom)
GET /api/v1/vendor/{code}/content-pages # List all pages
# Override/Custom Page Management
POST /api/v1/vendor/{code}/content-pages # Create override/custom
PUT /api/v1/vendor/{code}/content-pages/{id} # Update page
DELETE /api/v1/vendor/{code}/content-pages/{id} # Delete/revert page
# Page Preview
GET /api/v1/vendor/{code}/content-pages/{slug}/preview # Preview with fallback
Public API
# Storefront Pages (with fallback resolution)
GET /api/v1/shop/content-pages/{slug} # Get page (vendor context from middleware)
GET /api/v1/shop/content-pages/navigation # Get nav pages
# Platform Marketing Pages
GET /api/v1/platform/{code}/pages/{slug} # Get platform page
GET /api/v1/platform/{code}/pages/navigation # Get platform nav
Implementation Phases
Phase 1: Database & Model Updates
- Create
Platformmodel - Add
platform_idtoContentPage - Add
is_platform_pagetoContentPage - Add
platform_idtoVendor - Create migration scripts
- Create default "oms" platform for existing data
Phase 2: Service Layer
- Update
ContentPageServicewith three-tier resolution - Add
PlatformServicefor platform CRUD - Update page listing to show source (Platform/Default/Override)
Phase 3: Admin Interface
- Platform management UI (
/admin/platforms) - Platform pages editor (
/admin/platforms/{code}/pages) - Vendor defaults editor (
/admin/platforms/{code}/defaults) - Fix platform homepage to use CMS
Phase 4: Vendor Dashboard
- Update content pages list to show page source
- Add "Override" action for default pages
- Add "Revert to Default" action for overrides
- Visual indicator for inherited vs custom pages
Phase 5: Public Routes
- Update shop routes with three-tier resolution
- Update platform routes to use CMS
- Ensure proper navigation loading per context
Migration Strategy
Existing Data Handling
-- 1. Create default platform
INSERT INTO platforms (code, name, domain, is_active)
VALUES ('oms', 'Wizamart OMS', 'localhost:8000', true);
-- 2. Update all existing content_pages
UPDATE content_pages
SET platform_id = (SELECT id FROM platforms WHERE code = 'oms');
-- 3. Mark platform marketing pages
UPDATE content_pages
SET is_platform_page = true
WHERE vendor_id IS NULL
AND slug IN ('platform_homepage', 'pricing');
-- 4. Remaining NULL vendor pages become vendor defaults
UPDATE content_pages
SET is_platform_page = false
WHERE vendor_id IS NULL
AND is_platform_page IS NULL;
-- 5. Update all vendors
UPDATE vendors
SET platform_id = (SELECT id FROM platforms WHERE code = 'oms');
Open Questions
-
Domain routing: How to route requests to correct platform?
- Option A: Separate domains (oms.wizamart.lu, loyalty.wizamart.lu)
- Option B: Path-based (/oms/, /loyalty/)
- Option C: Subdomain detection
-
Shared vendors: Can a vendor belong to multiple platforms?
- Current assumption: NO, one vendor per platform
- If YES: Need junction table
-
Tier restrictions: Can page creation be restricted by tier?
- e.g., Basic tier: max 5 custom pages
- e.g., Pro tier: unlimited pages
-
Template inheritance: Should vendor defaults have template selection?
- Or always use a standard template?
Session Notes
2026-01-18
- Analyzed current CMS architecture issues
- Identified homepage is hardcoded (not using CMS)
- Confirmed admin platform-homepage UI is non-functional
- Designed three-tier content hierarchy:
- Platform pages (marketing)
- Vendor defaults (fallback)
- Vendor overrides/custom
- Documented user journeys for all actors
- Outlined implementation phases
- Next: Review proposal, clarify open questions, begin implementation
Document created for session continuity. Update as discussions progress.